Merge "Fix bugprone-use-after-move in ResourceParser"
diff --git a/Android.bp b/Android.bp
index 0c1095d..e1cb037 100644
--- a/Android.bp
+++ b/Android.bp
@@ -297,6 +297,7 @@
srcs: [
":framework-non-updatable-sources",
"core/java/**/*.logtags",
+ ":apex-info-list",
],
aidl: {
generate_get_transaction_name: true,
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 1e5ae7b..82759f7 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -112,7 +112,6 @@
}
// Defaults module for doc-stubs targets that use module source code as input.
-// This is the default/normal.
stubs_defaults {
name: "framework-doc-stubs-sources-default",
defaults: ["framework-doc-stubs-default"],
@@ -148,12 +147,6 @@
}
droidstubs {
- name: "framework-doc-stubs",
- defaults: ["framework-doc-stubs-sources-default"],
- args: metalava_framework_docs_args,
-}
-
-droidstubs {
name: "framework-doc-system-stubs",
defaults: ["framework-doc-stubs-sources-default"],
args: metalava_framework_docs_args +
@@ -161,11 +154,8 @@
api_levels_sdk_type: "system",
}
-// Experimental target building doc stubs with module stub source code as input.
-// This is intended to eventually replace framework-doc-stubs, once all diffs
-// have been eliminated.
droidstubs {
- name: "framework-doc-stubs-module-stubs",
+ name: "framework-doc-stubs",
defaults: ["framework-doc-stubs-default"],
args: metalava_framework_docs_args,
srcs: [
diff --git a/PACKAGE_MANAGER_OWNERS b/PACKAGE_MANAGER_OWNERS
new file mode 100644
index 0000000..e4549b4
--- /dev/null
+++ b/PACKAGE_MANAGER_OWNERS
@@ -0,0 +1,3 @@
+chiuwinson@google.com
+patb@google.com
+schfan@google.com
\ No newline at end of file
diff --git a/apct-tests/perftests/textclassifier/AndroidManifest.xml b/apct-tests/perftests/textclassifier/AndroidManifest.xml
index 7cf487f..a79f0f1 100644
--- a/apct-tests/perftests/textclassifier/AndroidManifest.xml
+++ b/apct-tests/perftests/textclassifier/AndroidManifest.xml
@@ -16,6 +16,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.perftests.textclassifier">
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
<application>
<uses-library android:name="android.test.runner" />
</application>
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index 035c9d2..b2e95a5 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -16,8 +16,6 @@
package com.android.server.tare;
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
-
import static com.android.server.tare.EconomicPolicy.REGULATION_BASIC_INCOME;
import static com.android.server.tare.EconomicPolicy.REGULATION_BIRTHRIGHT;
import static com.android.server.tare.EconomicPolicy.REGULATION_WEALTH_RECLAMATION;
@@ -69,12 +67,6 @@
private static final boolean DEBUG = InternalResourceService.DEBUG
|| Log.isLoggable(TAG, Log.DEBUG);
- /**
- * The minimum amount of time an app must not have been used by the user before we start
- * regularly reclaiming ARCs from it.
- */
- private static final long MIN_UNUSED_TIME_MS = 3 * 24 * HOUR_IN_MILLIS;
-
private static final String ALARM_TAG_AFFORDABILITY_CHECK = "*tare.affordability_check*";
private final Object mLock;
@@ -550,41 +542,62 @@
/**
* Reclaim a percentage of unused ARCs from every app that hasn't been used recently. The
- * reclamation will not reduce an app's balance below its minimum balance as dictated by the
- * EconomicPolicy.
+ * reclamation will not reduce an app's balance below its minimum balance as dictated by
+ * {@code scaleMinBalance}.
*
- * @param percentage A value between 0 and 1 to indicate how much of the unused balance should
- * be reclaimed.
+ * @param percentage A value between 0 and 1 to indicate how much of the unused balance
+ * should be reclaimed.
+ * @param minUnusedTimeMs The minimum amount of time (in milliseconds) that must have
+ * transpired since the last user usage event before we will consider
+ * reclaiming ARCs from the app.
+ * @param scaleMinBalance Whether or not to used the scaled minimum app balance. If false,
+ * this will use the constant min balance floor given by
+ * {@link EconomicPolicy#getMinSatiatedBalance(int, String)}. If true,
+ * this will use the scaled balance given by
+ * {@link InternalResourceService#getMinBalanceLocked(int, String)}.
*/
@GuardedBy("mLock")
- void reclaimUnusedAssetsLocked(double percentage) {
+ void reclaimUnusedAssetsLocked(double percentage, long minUnusedTimeMs,
+ boolean scaleMinBalance) {
final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
- final List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+ final SparseArrayMap<String, Ledger> ledgers = mScribe.getLedgersLocked();
final long now = getCurrentTimeMillis();
- for (int i = 0; i < pkgs.size(); ++i) {
- final int userId = UserHandle.getUserId(pkgs.get(i).applicationInfo.uid);
- final String pkgName = pkgs.get(i).packageName;
- final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
- // AppStandby only counts elapsed time for things like this
- // TODO: should we use clock time instead?
- final long timeSinceLastUsedMs =
- mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId);
- if (timeSinceLastUsedMs >= MIN_UNUSED_TIME_MS) {
- // Use a constant floor instead of the scaled floor from the IRS.
- final long minBalance = economicPolicy.getMinSatiatedBalance(userId, pkgName);
+ for (int u = 0; u < ledgers.numMaps(); ++u) {
+ final int userId = ledgers.keyAt(u);
+ for (int p = 0; p < ledgers.numElementsForKey(userId); ++p) {
+ final Ledger ledger = ledgers.valueAt(u, p);
final long curBalance = ledger.getCurrentBalance();
- long toReclaim = (long) (curBalance * percentage);
- if (curBalance - toReclaim < minBalance) {
- toReclaim = curBalance - minBalance;
+ if (curBalance <= 0) {
+ continue;
}
- if (toReclaim > 0) {
- Slog.i(TAG, "Reclaiming unused wealth! Taking " + toReclaim
- + " from " + appToString(userId, pkgName));
+ final String pkgName = ledgers.keyAt(u, p);
+ // AppStandby only counts elapsed time for things like this
+ // TODO: should we use clock time instead?
+ final long timeSinceLastUsedMs =
+ mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId);
+ if (timeSinceLastUsedMs >= minUnusedTimeMs) {
+ final long minBalance;
+ if (!scaleMinBalance) {
+ // Use a constant floor instead of the scaled floor from the IRS.
+ minBalance = economicPolicy.getMinSatiatedBalance(userId, pkgName);
+ } else {
+ minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
+ }
+ long toReclaim = (long) (curBalance * percentage);
+ if (curBalance - toReclaim < minBalance) {
+ toReclaim = curBalance - minBalance;
+ }
+ if (toReclaim > 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "Reclaiming unused wealth! Taking " + toReclaim
+ + " from " + appToString(userId, pkgName));
+ }
- recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(
- now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim),
- true);
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(
+ now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim),
+ true);
+ }
}
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index a39fd47..1ad7407 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -18,6 +18,7 @@
import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS;
import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS;
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -85,6 +86,11 @@
static final long UNUSED_RECLAMATION_PERIOD_MS = 24 * HOUR_IN_MILLIS;
/** How much of an app's unused wealth should be reclaimed periodically. */
private static final float DEFAULT_UNUSED_RECLAMATION_PERCENTAGE = .1f;
+ /**
+ * The minimum amount of time an app must not have been used by the user before we start
+ * periodically reclaiming ARCs from it.
+ */
+ private static final long MIN_UNUSED_TIME_MS = 3 * DAY_IN_MILLIS;
/** The amount of time to delay reclamation by after boot. */
private static final long RECLAMATION_STARTUP_DELAY_MS = 30_000L;
private static final int PACKAGE_QUERY_FLAGS =
@@ -107,6 +113,44 @@
@GuardedBy("mLock")
private CompleteEconomicPolicy mCompleteEconomicPolicy;
+ private static final class ReclamationConfig {
+ /**
+ * ARC circulation threshold (% circulating vs scaled maximum) above which this config
+ * should come into play.
+ */
+ public final double circulationPercentageThreshold;
+ /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */
+ public final double reclamationPercentage;
+ /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */
+ public final long minUsedTimeMs;
+ /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */
+ public final boolean scaleMinBalance;
+
+ ReclamationConfig(double circulationPercentageThreshold, double reclamationPercentage,
+ long minUsedTimeMs, boolean scaleMinBalance) {
+ this.circulationPercentageThreshold = circulationPercentageThreshold;
+ this.reclamationPercentage = reclamationPercentage;
+ this.minUsedTimeMs = minUsedTimeMs;
+ this.scaleMinBalance = scaleMinBalance;
+ }
+ }
+
+ /**
+ * Sorted list of reclamation configs used to determine how many credits to force reclaim when
+ * the circulation percentage is too high. The list should *always* be sorted in descending
+ * order of {@link ReclamationConfig#circulationPercentageThreshold}.
+ */
+ @GuardedBy("mLock")
+ private final List<ReclamationConfig> mReclamationConfigs = List.of(
+ new ReclamationConfig(2, .75, 12 * HOUR_IN_MILLIS, true),
+ new ReclamationConfig(1.6, .5, DAY_IN_MILLIS, true),
+ new ReclamationConfig(1.4, .25, DAY_IN_MILLIS, true),
+ new ReclamationConfig(1.2, .25, 2 * DAY_IN_MILLIS, true),
+ new ReclamationConfig(1, .25, MIN_UNUSED_TIME_MS, false),
+ new ReclamationConfig(
+ .9, DEFAULT_UNUSED_RECLAMATION_PERCENTAGE, MIN_UNUSED_TIME_MS, false)
+ );
+
@NonNull
@GuardedBy("mLock")
private final List<PackageInfo> mPkgCache = new ArrayList<>();
@@ -190,7 +234,8 @@
@Override
public void onAlarm() {
synchronized (mLock) {
- mAgent.reclaimUnusedAssetsLocked(DEFAULT_UNUSED_RECLAMATION_PERCENTAGE);
+ mAgent.reclaimUnusedAssetsLocked(
+ DEFAULT_UNUSED_RECLAMATION_PERCENTAGE, MIN_UNUSED_TIME_MS, false);
mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis());
scheduleUnusedWealthReclamationLocked();
}
@@ -200,6 +245,7 @@
private static final int MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER = 0;
private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1;
private static final int MSG_PROCESS_USAGE_EVENT = 2;
+ private static final int MSG_MAYBE_FOCE_RECLAIM = 3;
private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
private static final String KEY_PKG = "pkg";
@@ -317,6 +363,8 @@
final int newBatteryLevel = getCurrentBatteryLevel();
if (newBatteryLevel > mCurrentBatteryLevel) {
mAgent.distributeBasicIncomeLocked(newBatteryLevel);
+ } else if (newBatteryLevel < mCurrentBatteryLevel) {
+ mHandler.obtainMessage(MSG_MAYBE_FOCE_RECLAIM).sendToTarget();
}
mCurrentBatteryLevel = newBatteryLevel;
}
@@ -511,6 +559,48 @@
}
}
+ /**
+ * Reclaim unused ARCs above apps' minimum balances if there are too many credits currently
+ * in circulation.
+ */
+ @GuardedBy("mLock")
+ private void maybeForceReclaimLocked() {
+ final long maxCirculation = getMaxCirculationLocked();
+ if (maxCirculation == 0) {
+ Slog.wtf(TAG, "Max scaled circulation is 0...");
+ mAgent.reclaimUnusedAssetsLocked(1, HOUR_IN_MILLIS, true);
+ mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis());
+ scheduleUnusedWealthReclamationLocked();
+ return;
+ }
+ final long curCirculation = mScribe.getNarcsInCirculationLocked();
+ final double circulationPerc = 1.0 * curCirculation / maxCirculation;
+ if (DEBUG) {
+ Slog.d(TAG, "Circulation %: " + circulationPerc);
+ }
+ final int numConfigs = mReclamationConfigs.size();
+ if (numConfigs == 0) {
+ return;
+ }
+ // The configs are sorted in descending order of circulationPercentageThreshold, so we can
+ // short-circuit if the current circulation is lower than the lowest threshold.
+ if (circulationPerc
+ < mReclamationConfigs.get(numConfigs - 1).circulationPercentageThreshold) {
+ return;
+ }
+ // TODO: maybe exclude apps we think will be launched in the next few hours
+ for (int i = 0; i < numConfigs; ++i) {
+ final ReclamationConfig config = mReclamationConfigs.get(i);
+ if (circulationPerc >= config.circulationPercentageThreshold) {
+ mAgent.reclaimUnusedAssetsLocked(
+ config.reclamationPercentage, config.minUsedTimeMs, config.scaleMinBalance);
+ mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis());
+ scheduleUnusedWealthReclamationLocked();
+ break;
+ }
+ }
+ }
+
private void registerListeners() {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
@@ -594,6 +684,14 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_MAYBE_FOCE_RECLAIM: {
+ removeMessages(MSG_MAYBE_FOCE_RECLAIM);
+ synchronized (mLock) {
+ maybeForceReclaimLocked();
+ }
+ }
+ break;
+
case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: {
Bundle data = msg.getData();
final int userId = msg.arg1;
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
index d4c6c8c..42f3d9a 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -147,6 +147,12 @@
return ledger;
}
+ @GuardedBy("mIrs.getLock()")
+ @NonNull
+ SparseArrayMap<String, Ledger> getLedgersLocked() {
+ return mLedgers;
+ }
+
/** Returns the total amount of narcs currently allocated to apps. */
@GuardedBy("mIrs.getLock()")
long getNarcsInCirculationLocked() {
diff --git a/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java b/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java
index fb66609..915f3f6a3 100644
--- a/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java
+++ b/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java
@@ -43,7 +43,7 @@
*/
abstract class BaseMediaParceledListSlice<T> implements Parcelable {
private static String TAG = "BaseMediaParceledListSlice";
- private static boolean DEBUG = false;
+ private static final boolean DEBUG = false;
/*
* TODO get this number from somewhere else. For now set it to a quarter of
diff --git a/core/api/current.txt b/core/api/current.txt
index f989d06..0e948db 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8684,12 +8684,14 @@
method public android.bluetooth.BluetoothDevice getRemoteDevice(byte[]);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public int getScanMode();
method public int getState();
+ method public int isCisCentralSupported();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean isDiscovering();
method public boolean isEnabled();
method public boolean isLe2MPhySupported();
method public boolean isLeCodedPhySupported();
method public boolean isLeExtendedAdvertisingSupported();
method public boolean isLePeriodicAdvertisingSupported();
+ method public int isLePeriodicAdvertisingSyncTransferSenderSupported();
method public boolean isMultipleAdvertisementSupported();
method public boolean isOffloadedFilteringSupported();
method public boolean isOffloadedScanBatchingSupported();
@@ -9542,6 +9544,7 @@
field public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2; // 0x2
field public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; // 0x1
field public static final int ERROR_DEVICE_NOT_BONDED = 3; // 0x3
+ field public static final int ERROR_FEATURE_NOT_SUPPORTED = 8; // 0x8
field public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; // 0x6
field public static final int ERROR_UNKNOWN = 2147483647; // 0x7fffffff
field public static final int SUCCESS = 0; // 0x0
@@ -22672,7 +22675,7 @@
field public static final String KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder";
field public static final String KEY_MAX_HEIGHT = "max-height";
field public static final String KEY_MAX_INPUT_SIZE = "max-input-size";
- field public static final String KEY_MAX_OUTPUT_CHANNEL_COUNT = "max-output-channel_count";
+ field public static final String KEY_MAX_OUTPUT_CHANNEL_COUNT = "max-output-channel-count";
field public static final String KEY_MAX_PTS_GAP_TO_ENCODER = "max-pts-gap-to-encoder";
field public static final String KEY_MAX_WIDTH = "max-width";
field public static final String KEY_MIME = "mime";
@@ -30975,6 +30978,7 @@
field public static final int Q = 29; // 0x1d
field public static final int R = 30; // 0x1e
field public static final int S = 31; // 0x1f
+ field public static final int S_V2 = 32; // 0x20
field public static final int TIRAMISU = 10000; // 0x2710
}
@@ -31589,7 +31593,9 @@
method @NonNull public static android.os.Parcel obtain();
method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder);
method @Nullable public Object[] readArray(@Nullable ClassLoader);
+ method @Nullable public <T> T[] readArray(@Nullable ClassLoader, @NonNull Class<T>);
method @Nullable public java.util.ArrayList readArrayList(@Nullable ClassLoader);
+ method @Nullable public <T> java.util.ArrayList<T> readArrayList(@Nullable ClassLoader, @NonNull Class<? extends T>);
method public void readBinderArray(@NonNull android.os.IBinder[]);
method public void readBinderList(@NonNull java.util.List<android.os.IBinder>);
method public boolean readBoolean();
@@ -31617,6 +31623,7 @@
method @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 @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>);
method @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
@@ -31626,6 +31633,7 @@
method @NonNull public android.util.Size readSize();
method @NonNull public android.util.SizeF readSizeF();
method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
+ method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader, @NonNull Class<? extends T>);
method @Nullable public android.util.SparseBooleanArray readSparseBooleanArray();
method @Nullable public String readString();
method public void readStringArray(@NonNull String[]);
@@ -47696,6 +47704,10 @@
field public static final int KEYCODE_CUT = 277; // 0x115
field public static final int KEYCODE_D = 32; // 0x20
field public static final int KEYCODE_DEL = 67; // 0x43
+ field public static final int KEYCODE_DEMO_APP_1 = 301; // 0x12d
+ field public static final int KEYCODE_DEMO_APP_2 = 302; // 0x12e
+ field public static final int KEYCODE_DEMO_APP_3 = 303; // 0x12f
+ field public static final int KEYCODE_DEMO_APP_4 = 304; // 0x130
field public static final int KEYCODE_DPAD_CENTER = 23; // 0x17
field public static final int KEYCODE_DPAD_DOWN = 20; // 0x14
field public static final int KEYCODE_DPAD_DOWN_LEFT = 269; // 0x10d
@@ -47727,6 +47739,10 @@
field public static final int KEYCODE_F7 = 137; // 0x89
field public static final int KEYCODE_F8 = 138; // 0x8a
field public static final int KEYCODE_F9 = 139; // 0x8b
+ field public static final int KEYCODE_FEATURED_APP_1 = 297; // 0x129
+ field public static final int KEYCODE_FEATURED_APP_2 = 298; // 0x12a
+ field public static final int KEYCODE_FEATURED_APP_3 = 299; // 0x12b
+ field public static final int KEYCODE_FEATURED_APP_4 = 300; // 0x12c
field public static final int KEYCODE_FOCUS = 80; // 0x50
field public static final int KEYCODE_FORWARD = 125; // 0x7d
field public static final int KEYCODE_FORWARD_DEL = 112; // 0x70
@@ -47892,6 +47908,14 @@
field public static final int KEYCODE_U = 49; // 0x31
field public static final int KEYCODE_UNKNOWN = 0; // 0x0
field public static final int KEYCODE_V = 50; // 0x32
+ field public static final int KEYCODE_VIDEO_APP_1 = 289; // 0x121
+ field public static final int KEYCODE_VIDEO_APP_2 = 290; // 0x122
+ field public static final int KEYCODE_VIDEO_APP_3 = 291; // 0x123
+ field public static final int KEYCODE_VIDEO_APP_4 = 292; // 0x124
+ field public static final int KEYCODE_VIDEO_APP_5 = 293; // 0x125
+ field public static final int KEYCODE_VIDEO_APP_6 = 294; // 0x126
+ field public static final int KEYCODE_VIDEO_APP_7 = 295; // 0x127
+ field public static final int KEYCODE_VIDEO_APP_8 = 296; // 0x128
field public static final int KEYCODE_VOICE_ASSIST = 231; // 0xe7
field public static final int KEYCODE_VOLUME_DOWN = 25; // 0x19
field public static final int KEYCODE_VOLUME_MUTE = 164; // 0xa4
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 599f11d..95d06b0 100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -95,6 +95,7 @@
field public static final String CONTROL_OEM_PAID_NETWORK_PREFERENCE = "android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE";
field public static final String CONTROL_VPN = "android.permission.CONTROL_VPN";
field public static final String CREATE_USERS = "android.permission.CREATE_USERS";
+ field public static final String CREATE_VIRTUAL_DEVICE = "android.permission.CREATE_VIRTUAL_DEVICE";
field public static final String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER";
field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER";
field public static final String DISABLE_SYSTEM_SOUND_EFFECTS = "android.permission.DISABLE_SYSTEM_SOUND_EFFECTS";
@@ -1948,6 +1949,22 @@
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
}
+ public final class BluetoothActivityEnergyInfo implements android.os.Parcelable {
+ method public int getBluetoothStackState();
+ method public long getControllerEnergyUsed();
+ method public long getControllerIdleTimeMillis();
+ method public long getControllerRxTimeMillis();
+ method public long getControllerTxTimeMillis();
+ method public long getTimestampMillis();
+ method @NonNull public java.util.List<android.bluetooth.UidTraffic> getUidTraffic();
+ method public boolean isValid();
+ field public static final int BT_STACK_STATE_INVALID = 0; // 0x0
+ field public static final int BT_STACK_STATE_STATE_ACTIVE = 1; // 0x1
+ field public static final int BT_STACK_STATE_STATE_IDLE = 3; // 0x3
+ field public static final int BT_STACK_STATE_STATE_SCANNING = 2; // 0x2
+ field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothActivityEnergyInfo> CREATOR;
+ }
+
public final class BluetoothAdapter {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean addOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean disable(boolean);
@@ -2247,6 +2264,13 @@
method @NonNull public android.bluetooth.OobData.LeBuilder setRandomizerHash(@NonNull byte[]);
}
+ public final class UidTraffic implements java.lang.Cloneable android.os.Parcelable {
+ method public long getRxBytes();
+ method public long getTxBytes();
+ method public int getUid();
+ field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.UidTraffic> CREATOR;
+ }
+
}
package android.bluetooth.le {
@@ -2310,6 +2334,17 @@
}
+package android.companion.virtual {
+
+ public final class VirtualDeviceManager {
+ }
+
+ public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
+ method public void close();
+ }
+
+}
+
package android.content {
public class ApexEnvironment {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e707b03..72e93f8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -337,6 +337,7 @@
}
public class StatusBarManager {
+ method public void cancelRequestAddTile(@NonNull String);
method public void clickNotification(@Nullable String, int, int, boolean);
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void collapsePanels();
method public void expandNotificationsPanel();
@@ -2785,7 +2786,7 @@
method public static String actionToString(int);
method public final void setDisplayId(int);
field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800
- field public static final int LAST_KEYCODE = 288; // 0x120
+ field public static final int LAST_KEYCODE = 304; // 0x130
}
public final class KeyboardShortcutGroup implements android.os.Parcelable {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index e3909f9..0881db0 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -518,6 +518,11 @@
Notification notification, int id, String pkg, @UserIdInt int userId);
/**
+ * Un-foreground all foreground services in the given app.
+ */
+ public abstract void makeServicesNonForeground(String pkg, @UserIdInt int userId);
+
+ /**
* If the given app has any FGSs whose notifications are in the given channel,
* stop them.
*/
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index 08cd0b3..53b16d3 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -48,18 +48,19 @@
ClassLoader parent, String classLoaderName) {
return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
librarySearchPath, libraryPermittedPath, parent, classLoaderName,
- null, null);
+ null, null, null);
}
ClassLoader getClassLoaderWithSharedLibraries(
String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String classLoaderName,
- List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries,
+ List<ClassLoader> sharedLibrariesLoadedAfterApp) {
// For normal usage the cache key used is the same as the zip path.
return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries,
- nativeSharedLibraries);
+ nativeSharedLibraries, sharedLibrariesLoadedAfterApp);
}
/**
@@ -71,7 +72,8 @@
*/
ClassLoader getSharedLibraryClassLoaderWithSharedLibraries(String zip, int targetSdkVersion,
boolean isBundled, String librarySearchPath, String libraryPermittedPath,
- ClassLoader parent, String classLoaderName, List<ClassLoader> sharedLibraries) {
+ ClassLoader parent, String classLoaderName, List<ClassLoader> sharedLibraries,
+ List<ClassLoader> sharedLibrariesAfter) {
ClassLoader loader = getCachedNonBootclasspathSystemLib(zip, parent, classLoaderName,
sharedLibraries);
if (loader != null) {
@@ -86,14 +88,15 @@
nativeSharedLibraries.add("ALL");
return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries,
- nativeSharedLibraries);
+ nativeSharedLibraries, sharedLibrariesAfter);
}
private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String cacheKey,
String classLoaderName, List<ClassLoader> sharedLibraries,
- List<String> nativeSharedLibraries) {
+ List<String> nativeSharedLibraries,
+ List<ClassLoader> sharedLibrariesLoadedAfterApp) {
/*
* This is the parent we use if they pass "null" in. In theory
* this should be the "system" class loader; in practice we
@@ -123,7 +126,7 @@
ClassLoader classloader = ClassLoaderFactory.createClassLoader(
zip, librarySearchPath, libraryPermittedPath, parent,
targetSdkVersion, isBundled, classLoaderName, sharedLibraries,
- nativeSharedLibraries);
+ nativeSharedLibraries, sharedLibrariesLoadedAfterApp);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -140,7 +143,8 @@
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
ClassLoader loader = ClassLoaderFactory.createClassLoader(
- zip, null, parent, classLoaderName, sharedLibraries);
+ zip, null, parent, classLoaderName, sharedLibraries,
+ null /*sharedLibrariesLoadedAfterApp*/);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return loader;
}
@@ -196,7 +200,7 @@
ClassLoader classLoader = getClassLoader(path, Build.VERSION.SDK_INT, true /*isBundled*/,
null /*librarySearchPath*/, null /*libraryPermittedPath*/, null /*parent*/,
null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/,
- null /* nativeSharedLibraries */);
+ null /* nativeSharedLibraries */, null /*sharedLibrariesLoadedAfterApp*/);
if (classLoader == null) {
// bad configuration or break in classloading code
@@ -267,7 +271,8 @@
// stub's APK path, when the actual package path is the donor APK.
return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
cacheKey, null /* classLoaderName */, null /* sharedLibraries */,
- null /* nativeSharedLibraries */);
+ null /* nativeSharedLibraries */,
+ null /*sharedLibrariesLoadedAfterApp*/);
}
/**
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index bd43ab5..756833a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2763,7 +2763,7 @@
*/
private static Resources createWindowContextResources(@NonNull ContextImpl windowContextBase) {
final LoadedApk packageInfo = windowContextBase.mPackageInfo;
- final ClassLoader classLoader = windowContextBase.mClassLoader;
+ final ClassLoader classLoader = windowContextBase.getClassLoader();
final IBinder token = windowContextBase.getWindowContextToken();
final String resDir = packageInfo.getResDir();
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 47e4810..658c130 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -278,6 +278,7 @@
List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState();
boolean clearApplicationUserData(in String packageName, boolean keepState,
in IPackageDataObserver observer, int userId);
+ void makeServicesNonForeground(in String packageName, int userId);
@UnsupportedAppUsage
void forceStopPackage(in String packageName, int userId);
boolean killPids(in int[] pids, in String reason, boolean secure);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 2be7803..b052bc5 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -68,6 +68,7 @@
import android.os.WorkSource;
import android.service.voice.IVoiceInteractionSession;
import android.view.IRecentsAnimationRunner;
+import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationAdapter;
import android.window.IWindowOrganizerController;
@@ -344,4 +345,9 @@
* @param caller is the IApplicationThread representing the calling process.
*/
void setRunningRemoteTransitionDelegate(in IApplicationThread caller);
+
+ /**
+ * Prepare the back preview in the server
+ */
+ void startBackPreview(IRemoteAnimationRunner runner);
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index a2c9795..5750484 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -54,10 +54,12 @@
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.view.DisplayAdjustments;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
@@ -76,6 +78,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
+import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
@@ -700,7 +703,7 @@
ClassLoader createSharedLibraryLoader(SharedLibraryInfo sharedLibrary,
boolean isBundledApp, String librarySearchPath, String libraryPermittedPath) {
List<String> paths = sharedLibrary.getAllCodePaths();
- List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
+ Pair<List<ClassLoader>, List<ClassLoader>> sharedLibraries = createSharedLibrariesLoaders(
sharedLibrary.getDependencies(), isBundledApp, librarySearchPath,
libraryPermittedPath);
final String jars = (paths.size() == 1) ? paths.get(0) :
@@ -711,15 +714,31 @@
return ApplicationLoaders.getDefault().getSharedLibraryClassLoaderWithSharedLibraries(jars,
mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, /* parent */ null,
- /* classLoaderName */ null, sharedLibraries);
+ /* classLoaderName */ null, sharedLibraries.first, sharedLibraries.second);
}
- private List<ClassLoader> createSharedLibrariesLoaders(List<SharedLibraryInfo> sharedLibraries,
+ /**
+ *
+ * @return a {@link Pair} of List<ClassLoader> where the first is for standard shared libraries
+ * and the second is list for shared libraries that code should be loaded after the dex
+ */
+ private Pair<List<ClassLoader>, List<ClassLoader>> createSharedLibrariesLoaders(
+ List<SharedLibraryInfo> sharedLibraries,
boolean isBundledApp, String librarySearchPath, String libraryPermittedPath) {
- if (sharedLibraries == null) {
- return null;
+ if (sharedLibraries == null || sharedLibraries.isEmpty()) {
+ return new Pair<>(null, null);
}
+
+ // if configured to do so, shared libs are split into 2 collections: those that are
+ // on the class path before the applications code, which is standard, and those
+ // specified to be loaded after the applications code.
+ HashSet<String> libsToLoadAfter = new HashSet<>();
+ Resources systemR = Resources.getSystem();
+ Collections.addAll(libsToLoadAfter, systemR.getStringArray(
+ R.array.config_sharedLibrariesLoadedAfterApp));
+
List<ClassLoader> loaders = new ArrayList<>();
+ List<ClassLoader> after = new ArrayList<>();
for (SharedLibraryInfo info : sharedLibraries) {
if (info.isNative()) {
// Native shared lib doesn't contribute to the native lib search path. Its name is
@@ -727,10 +746,19 @@
// default linker namespace.
continue;
}
- loaders.add(createSharedLibraryLoader(
- info, isBundledApp, librarySearchPath, libraryPermittedPath));
+ if (libsToLoadAfter.contains(info.getName())) {
+ if (DEBUG) {
+ Slog.v(ActivityThread.TAG,
+ info.getName() + " will be loaded after application code");
+ }
+ after.add(createSharedLibraryLoader(
+ info, isBundledApp, librarySearchPath, libraryPermittedPath));
+ } else {
+ loaders.add(createSharedLibraryLoader(
+ info, isBundledApp, librarySearchPath, libraryPermittedPath));
+ }
}
- return loaders;
+ return new Pair<>(loaders, after);
}
private StrictMode.ThreadPolicy allowThreadDiskReads() {
@@ -955,9 +983,9 @@
// as this is early and necessary.
StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
- List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
- mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
- libraryPermittedPath);
+ Pair<List<ClassLoader>, List<ClassLoader>> sharedLibraries =
+ createSharedLibrariesLoaders(mApplicationInfo.sharedLibraryInfos, isBundledApp,
+ librarySearchPath, libraryPermittedPath);
List<String> nativeSharedLibraries = new ArrayList<>();
if (mApplicationInfo.sharedLibraryInfos != null) {
@@ -971,7 +999,8 @@
mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, mBaseClassLoader,
- mApplicationInfo.classLoaderName, sharedLibraries, nativeSharedLibraries);
+ mApplicationInfo.classLoaderName, sharedLibraries.first, nativeSharedLibraries,
+ sharedLibraries.second);
mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
setThreadPolicy(oldPolicy);
@@ -2134,4 +2163,38 @@
final IBinder mService;
}
}
+
+ /**
+ * Check if the Apk paths in the cache are correct, and update them if they are not.
+ * @hide
+ */
+ public static void checkAndUpdateApkPaths(ApplicationInfo expectedAppInfo) {
+ // Get the LoadedApk from the cache
+ ActivityThread activityThread = ActivityThread.currentActivityThread();
+ if (activityThread == null) {
+ Log.e(TAG, "Cannot find activity thread");
+ return;
+ }
+ checkAndUpdateApkPaths(activityThread, expectedAppInfo, /* cacheWithCode */ true);
+ checkAndUpdateApkPaths(activityThread, expectedAppInfo, /* cacheWithCode */ false);
+ }
+
+ private static void checkAndUpdateApkPaths(ActivityThread activityThread,
+ ApplicationInfo expectedAppInfo, boolean cacheWithCode) {
+ String expectedCodePath = expectedAppInfo.getCodePath();
+ LoadedApk loadedApk = activityThread.peekPackageInfo(
+ expectedAppInfo.packageName, /* includeCode= */ cacheWithCode);
+ // If there is load apk cached, or if the cache is valid, don't do anything.
+ if (loadedApk == null || loadedApk.getApplicationInfo() == null
+ || loadedApk.getApplicationInfo().getCodePath().equals(expectedCodePath)) {
+ return;
+ }
+ // Duplicate framework logic
+ List<String> oldPaths = new ArrayList<>();
+ LoadedApk.makePaths(activityThread, expectedAppInfo, oldPaths);
+
+ // Force update the LoadedApk instance, which should update the reference in the cache
+ loadedApk.updateApplicationInfo(expectedAppInfo, oldPaths);
+ }
+
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 9be4adc..7dbd814 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -2579,6 +2579,9 @@
* for more information.
* </p>
* <p>
+ * Callers of this method must have notification listener access or permission to read contacts.
+ * </p>
+ * <p>
* NOTE: This method calls into Contacts, which may take some time, and should not be called
* on the main thread.
* </p>
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 64f89bc..48ceef0 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -669,6 +669,21 @@
}
}
+ /**
+ * @hide
+ * @param packageName
+ */
+ @TestApi
+ public void cancelRequestAddTile(@NonNull String packageName) {
+ Objects.requireNonNull(packageName);
+ IStatusBarService svc = getService();
+ try {
+ svc.cancelRequestAddTile(packageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
public static String windowStateToString(int state) {
if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 8ea1f83..890a8d6 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -54,6 +54,8 @@
import android.bluetooth.BluetoothManager;
import android.companion.CompanionDeviceManager;
import android.companion.ICompanionDeviceManager;
+import android.companion.virtual.IVirtualDeviceManager;
+import android.companion.virtual.VirtualDeviceManager;
import android.content.ClipboardManager;
import android.content.ContentCaptureOptions;
import android.content.Context;
@@ -875,6 +877,16 @@
return new CompanionDeviceManager(service, ctx.getOuterContext());
}});
+ registerService(Context.VIRTUAL_DEVICE_SERVICE, VirtualDeviceManager.class,
+ new CachedServiceFetcher<VirtualDeviceManager>() {
+ @Override
+ public VirtualDeviceManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IVirtualDeviceManager service = IVirtualDeviceManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.VIRTUAL_DEVICE_SERVICE));
+ return new VirtualDeviceManager(service, ctx.getOuterContext());
+ }});
+
registerService(Context.CONSUMER_IR_SERVICE, ConsumerIrManager.class,
new CachedServiceFetcher<ConsumerIrManager>() {
@Override
@@ -1925,39 +1937,6 @@
public abstract T createService() throws ServiceNotFoundException;
}
- /**
- * Like StaticServiceFetcher, creates only one instance of the service per application, but when
- * creating the service for the first time, passes it the application context of the creating
- * application.
- *
- * TODO: Delete this once its only user (ConnectivityManager) is known to work well in the
- * case where multiple application components each have their own ConnectivityManager object.
- */
- static abstract class StaticApplicationContextServiceFetcher<T> implements ServiceFetcher<T> {
- private T mCachedInstance;
-
- @Override
- public final T getService(ContextImpl ctx) {
- synchronized (StaticApplicationContextServiceFetcher.this) {
- if (mCachedInstance == null) {
- Context appContext = ctx.getApplicationContext();
- // If the application context is null, we're either in the system process or
- // it's the application context very early in app initialization. In both these
- // cases, the passed-in ContextImpl will not be freed, so it's safe to pass it
- // to the service. http://b/27532714 .
- try {
- mCachedInstance = createService(appContext != null ? appContext : ctx);
- } catch (ServiceNotFoundException e) {
- onServiceNotFound(e);
- }
- }
- return mCachedInstance;
- }
- }
-
- public abstract T createService(Context applicationContext) throws ServiceNotFoundException;
- }
-
/** @hide */
public static void onServiceNotFound(ServiceNotFoundException e) {
// We're mostly interested in tracking down long-lived core system
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index a53ef1b..b3fad21 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -108,7 +108,7 @@
@SystemService(Context.WALLPAPER_SERVICE)
public class WallpaperManager {
private static String TAG = "WallpaperManager";
- private static boolean DEBUG = false;
+ private static final boolean DEBUG = false;
private float mWallpaperXStep = -1;
private float mWallpaperYStep = -1;
private static final @NonNull RectF LOCAL_COLOR_BOUNDS =
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 4da1acb..5497b78 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -41,6 +41,7 @@
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -223,10 +224,15 @@
public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) {
ContentResolver resolver = mContext.getContentResolver();
try (ContentProviderClient provider = resolver.acquireUnstableContentProviderClient(uri)) {
- Bundle extras = new Bundle();
- extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
- final Bundle res = provider.call(SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
- return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
+ if (provider == null) {
+ Log.w(TAG, TextUtils.formatSimple("Unknown URI: %s", uri));
+ } else {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+ final Bundle res = provider.call(
+ SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
+ return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
+ }
} catch (RemoteException e) {
Log.e(TAG, "Unable to get slice descendants", e);
}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index dd147cc..eb10f09 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityOptions;
+import android.app.LoadedApk;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -554,7 +555,7 @@
}
// Prepare a local reference to the remote Context so we're ready to
// inflate any requested LayoutParams.
- mRemoteContext = getRemoteContext();
+ mRemoteContext = getRemoteContextEnsuringCorrectCachedApkPath();
int layoutId = rvToApply.getLayoutId();
if (rvToApply.canRecycleView(mView)) {
@@ -616,7 +617,7 @@
private void inflateAsync(@NonNull RemoteViews remoteViews) {
// Prepare a local reference to the remote Context so we're ready to
// inflate any requested LayoutParams.
- mRemoteContext = getRemoteContext();
+ mRemoteContext = getRemoteContextEnsuringCorrectCachedApkPath();
int layoutId = remoteViews.getLayoutId();
if (mLastExecutionSignal != null) {
@@ -718,8 +719,10 @@
* purposes of reading remote resources.
* @hide
*/
- protected Context getRemoteContext() {
+ protected Context getRemoteContextEnsuringCorrectCachedApkPath() {
try {
+ ApplicationInfo expectedAppInfo = mInfo.providerInfo.applicationInfo;
+ LoadedApk.checkAndUpdateApkPaths(expectedAppInfo);
// Return if cloned successfully, otherwise default
Context newContext = mContext.createApplicationContext(
mInfo.providerInfo.applicationInfo,
@@ -765,7 +768,7 @@
try {
if (mInfo != null) {
- Context theirContext = getRemoteContext();
+ Context theirContext = getRemoteContextEnsuringCorrectCachedApkPath();
mRemoteContext = theirContext;
LayoutInflater inflater = (LayoutInflater)
theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
diff --git a/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java
index df065bf..c17a7b4 100644
--- a/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java
+++ b/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java
@@ -16,18 +16,26 @@
package android.bluetooth;
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import java.util.Arrays;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
/**
* Record of energy and activity information from controller and
* underlying bt stack state.Timestamp the record with system
- * time
+ * time.
*
* @hide
*/
+@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
public final class BluetoothActivityEnergyInfo implements Parcelable {
private final long mTimestamp;
private int mBluetoothStackState;
@@ -35,13 +43,24 @@
private long mControllerRxTimeMs;
private long mControllerIdleTimeMs;
private long mControllerEnergyUsed;
- private UidTraffic[] mUidTraffic;
+ private List<UidTraffic> mUidTraffic;
+
+ /** @hide */
+ @IntDef(prefix = { "BT_STACK_STATE_" }, value = {
+ BT_STACK_STATE_INVALID,
+ BT_STACK_STATE_STATE_ACTIVE,
+ BT_STACK_STATE_STATE_SCANNING,
+ BT_STACK_STATE_STATE_IDLE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BluetoothStackState {}
public static final int BT_STACK_STATE_INVALID = 0;
public static final int BT_STACK_STATE_STATE_ACTIVE = 1;
public static final int BT_STACK_STATE_STATE_SCANNING = 2;
public static final int BT_STACK_STATE_STATE_IDLE = 3;
+ /** @hide */
public BluetoothActivityEnergyInfo(long timestamp, int stackState,
long txTime, long rxTime, long idleTime, long energyUsed) {
mTimestamp = timestamp;
@@ -52,17 +71,18 @@
mControllerEnergyUsed = energyUsed;
}
- @SuppressWarnings("unchecked")
- BluetoothActivityEnergyInfo(Parcel in) {
+ /** @hide */
+ private BluetoothActivityEnergyInfo(Parcel in) {
mTimestamp = in.readLong();
mBluetoothStackState = in.readInt();
mControllerTxTimeMs = in.readLong();
mControllerRxTimeMs = in.readLong();
mControllerIdleTimeMs = in.readLong();
mControllerEnergyUsed = in.readLong();
- mUidTraffic = in.createTypedArray(UidTraffic.CREATOR);
+ mUidTraffic = in.createTypedArrayList(UidTraffic.CREATOR);
}
+ /** @hide */
@Override
public String toString() {
return "BluetoothActivityEnergyInfo{"
@@ -72,11 +92,11 @@
+ " mControllerRxTimeMs=" + mControllerRxTimeMs
+ " mControllerIdleTimeMs=" + mControllerIdleTimeMs
+ " mControllerEnergyUsed=" + mControllerEnergyUsed
- + " mUidTraffic=" + Arrays.toString(mUidTraffic)
+ + " mUidTraffic=" + mUidTraffic
+ " }";
}
- public static final @android.annotation.NonNull Parcelable.Creator<BluetoothActivityEnergyInfo> CREATOR =
+ public static final @NonNull Parcelable.Creator<BluetoothActivityEnergyInfo> CREATOR =
new Parcelable.Creator<BluetoothActivityEnergyInfo>() {
public BluetoothActivityEnergyInfo createFromParcel(Parcel in) {
return new BluetoothActivityEnergyInfo(in);
@@ -87,9 +107,8 @@
}
};
-
+ /** @hide */
@Override
- @SuppressWarnings("unchecked")
public void writeToParcel(Parcel out, int flags) {
out.writeLong(mTimestamp);
out.writeInt(mBluetoothStackState);
@@ -97,17 +116,21 @@
out.writeLong(mControllerRxTimeMs);
out.writeLong(mControllerIdleTimeMs);
out.writeLong(mControllerEnergyUsed);
- out.writeTypedArray(mUidTraffic, flags);
+ out.writeTypedList(mUidTraffic);
}
+ /** @hide */
@Override
public int describeContents() {
return 0;
}
/**
- * @return bt stack reported state
+ * Get the Bluetooth stack state associated with the energy info.
+ *
+ * @return one of {@link #BluetoothStackState} states
*/
+ @BluetoothStackState
public int getBluetoothStackState() {
return mBluetoothStackState;
}
@@ -134,7 +157,7 @@
}
/**
- * product of current(mA), voltage(V) and time(ms)
+ * Get the product of current (mA), voltage (V), and time (ms).
*
* @return energy used
*/
@@ -143,22 +166,31 @@
}
/**
- * @return timestamp(real time elapsed in milliseconds since boot) of record creation.
+ * @return timestamp (real time elapsed in milliseconds since boot) of record creation
*/
- public long getTimeStamp() {
+ public @ElapsedRealtimeLong long getTimestampMillis() {
return mTimestamp;
}
- public UidTraffic[] getUidTraffic() {
+ /**
+ * Get the {@link List} of each application {@link android.bluetooth.UidTraffic}.
+ *
+ * @return current {@link List} of {@link android.bluetooth.UidTraffic}
+ */
+ public @NonNull List<UidTraffic> getUidTraffic() {
+ if (mUidTraffic == null) {
+ return Collections.emptyList();
+ }
return mUidTraffic;
}
- public void setUidTraffic(UidTraffic[] traffic) {
+ /** @hide */
+ public void setUidTraffic(List<UidTraffic> traffic) {
mUidTraffic = traffic;
}
/**
- * @return if the record is valid
+ * @return true if the record Tx time, Rx time, and Idle time are more than 0.
*/
public boolean isValid() {
return ((mControllerTxTimeMs >= 0) && (mControllerRxTimeMs >= 0)
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 06ce053..dac8ffe 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2272,6 +2272,66 @@
return false;
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ BluetoothStatusCodes.SUCCESS,
+ BluetoothStatusCodes.ERROR_UNKNOWN,
+ BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
+ BluetoothStatusCodes.ERROR_FEATURE_NOT_SUPPORTED,
+ })
+ public @interface LeFeatureReturnValues {}
+
+ /**
+ * Returns {@link BluetoothStatusCodes#SUCCESS} if LE Connected Isochronous Stream Central
+ * feature is supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if
+ * the feature is not supported or an error code.
+ *
+ * @return whether the chipset supports the LE Connected Isochronous Stream Central feature
+ */
+ @RequiresNoPermission
+ public @LeFeatureReturnValues int isCisCentralSupported() {
+ if (!getLeAccess()) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ return mService.isCisCentralSupported();
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return BluetoothStatusCodes.ERROR_UNKNOWN;
+ }
+
+ /**
+ * Returns {@link BluetoothStatusCodes#SUCCESS} if LE Periodic Advertising Sync Transfer Sender
+ * feature is supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if the
+ * feature is not supported or an error code
+ *
+ * @return whether the chipset supports the LE Periodic Advertising Sync Transfer Sender feature
+ */
+ @RequiresNoPermission
+ public @LeFeatureReturnValues int isLePeriodicAdvertisingSyncTransferSenderSupported() {
+ if (!getLeAccess()) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ return mService.isLePeriodicAdvertisingSyncTransferSenderSupported();
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return BluetoothStatusCodes.ERROR_UNKNOWN;
+ }
+
/**
* Return the maximum LE advertising data length in bytes,
* if LE Extended Advertising feature is supported, 0 otherwise.
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index e968052..6e918bd 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1186,6 +1186,11 @@
mAttributionSource = attributionSource;
}
+ /** {@hide} */
+ public void prepareToEnterProcess(@NonNull AttributionSource attributionSource) {
+ setAttributionSource(attributionSource);
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothDevice) {
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index b5df4db..20152f3 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -62,15 +62,15 @@
private static final String TAG = "BluetoothManager";
private static final boolean DBG = false;
- private static AttributionSource sAttributionSource = null;
+ private final AttributionSource mAttributionSource;
private final BluetoothAdapter mAdapter;
/**
* @hide
*/
public BluetoothManager(Context context) {
- sAttributionSource = resolveAttributionSource(context);
- mAdapter = BluetoothAdapter.createAdapter(sAttributionSource);
+ mAttributionSource = resolveAttributionSource(context);
+ mAdapter = BluetoothAdapter.createAdapter(mAttributionSource);
}
/** {@hide} */
@@ -79,9 +79,6 @@
if (context != null) {
res = context.getAttributionSource();
}
- else if (sAttributionSource != null) {
- return sAttributionSource;
- }
if (res == null) {
res = ActivityThread.currentAttributionSource();
}
@@ -201,8 +198,8 @@
IBluetoothGatt iGatt = managerService.getBluetoothGatt();
if (iGatt == null) return devices;
devices = Attributable.setAttributionSource(
- iGatt.getDevicesMatchingConnectionStates(states, sAttributionSource),
- sAttributionSource);
+ iGatt.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
diff --git a/core/java/android/bluetooth/BluetoothStatusCodes.java b/core/java/android/bluetooth/BluetoothStatusCodes.java
index 3e46c49..63e84ed 100644
--- a/core/java/android/bluetooth/BluetoothStatusCodes.java
+++ b/core/java/android/bluetooth/BluetoothStatusCodes.java
@@ -79,6 +79,11 @@
public static final int ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION = 7;
/**
+ * Error code indicating that the feature is not supported.
+ */
+ public static final int ERROR_FEATURE_NOT_SUPPORTED = 8;
+
+ /**
* If another application has already requested {@link OobData} then another fetch will be
* disallowed until the callback is removed.
*
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 858819e..bb537dd 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -189,7 +189,7 @@
@NonNull
@SystemApi
public static final ParcelUuid CAP =
- ParcelUuid.fromString("EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE");
+ ParcelUuid.fromString("00008FE0-0000-1000-8000-00805F9B34FB");
/** @hide */
@NonNull
@SystemApi
diff --git a/core/java/android/bluetooth/UidTraffic.java b/core/java/android/bluetooth/UidTraffic.java
index 2ee786a..9982fa6 100644
--- a/core/java/android/bluetooth/UidTraffic.java
+++ b/core/java/android/bluetooth/UidTraffic.java
@@ -15,6 +15,7 @@
*/
package android.bluetooth;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,27 +24,27 @@
*
* @hide
*/
-public class UidTraffic implements Cloneable, Parcelable {
+@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+public final class UidTraffic implements Cloneable, Parcelable {
private final int mAppUid;
private long mRxBytes;
private long mTxBytes;
- public UidTraffic(int appUid) {
- mAppUid = appUid;
- }
-
+ /** @hide */
public UidTraffic(int appUid, long rx, long tx) {
mAppUid = appUid;
mRxBytes = rx;
mTxBytes = tx;
}
- UidTraffic(Parcel in) {
+ /** @hide */
+ private UidTraffic(Parcel in) {
mAppUid = in.readInt();
mRxBytes = in.readLong();
mTxBytes = in.readLong();
}
+ /** @hide */
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mAppUid);
@@ -51,44 +52,60 @@
dest.writeLong(mTxBytes);
}
+ /** @hide */
public void setRxBytes(long bytes) {
mRxBytes = bytes;
}
+ /** @hide */
public void setTxBytes(long bytes) {
mTxBytes = bytes;
}
+ /** @hide */
public void addRxBytes(long bytes) {
mRxBytes += bytes;
}
+ /** @hide */
public void addTxBytes(long bytes) {
mTxBytes += bytes;
}
+ /**
+ * @return corresponding app Uid
+ */
public int getUid() {
return mAppUid;
}
+ /**
+ * @return rx bytes count
+ */
public long getRxBytes() {
return mRxBytes;
}
+ /**
+ * @return tx bytes count
+ */
public long getTxBytes() {
return mTxBytes;
}
+ /** @hide */
@Override
public int describeContents() {
return 0;
}
+ /** @hide */
@Override
public UidTraffic clone() {
return new UidTraffic(mAppUid, mRxBytes, mTxBytes);
}
+ /** @hide */
@Override
public String toString() {
return "UidTraffic{mAppUid=" + mAppUid + ", mRxBytes=" + mRxBytes + ", mTxBytes="
diff --git a/core/java/android/bluetooth/le/BluetoothLeUtils.java b/core/java/android/bluetooth/le/BluetoothLeUtils.java
index 6381f55..ed50b09 100644
--- a/core/java/android/bluetooth/le/BluetoothLeUtils.java
+++ b/core/java/android/bluetooth/le/BluetoothLeUtils.java
@@ -24,6 +24,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.UUID;
/**
* Helper class for Bluetooth LE utils.
@@ -137,4 +138,21 @@
}
}
+ /**
+ * Compares two UUIDs with a UUID mask.
+ *
+ * @param data first {@link #UUID} to compare.
+ * @param uuid second {@link #UUID} to compare.
+ * @param mask mask {@link #UUID}.
+ * @return true if both UUIDs are equals when masked, false otherwise.
+ */
+ static boolean maskedEquals(UUID data, UUID uuid, UUID mask) {
+ if (mask == null) {
+ return Objects.equals(data, uuid);
+ }
+ return (data.getLeastSignificantBits() & mask.getLeastSignificantBits())
+ == (uuid.getLeastSignificantBits() & mask.getLeastSignificantBits())
+ && (data.getMostSignificantBits() & mask.getMostSignificantBits())
+ == (uuid.getMostSignificantBits() & mask.getMostSignificantBits());
+ }
}
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 8ff0181..b059193 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -28,8 +28,6 @@
import android.os.ParcelUuid;
import android.os.Parcelable;
-import com.android.internal.util.BitUtils;
-
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -448,7 +446,7 @@
// Check if the uuid pattern matches the particular service uuid.
private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) {
- return BitUtils.maskedEquals(data, uuid, mask);
+ return BluetoothLeUtils.maskedEquals(data, uuid, mask);
}
/**
@@ -478,7 +476,7 @@
// Check if the solicitation uuid pattern matches the particular service solicitation uuid.
private static boolean matchesServiceSolicitationUuid(UUID solicitationUuid,
UUID solicitationUuidMask, UUID data) {
- return BitUtils.maskedEquals(data, solicitationUuid, solicitationUuidMask);
+ return BluetoothLeUtils.maskedEquals(data, solicitationUuid, solicitationUuidMask);
}
// Check whether the data pattern matches the parsed data.
diff --git a/core/java/android/companion/Association.aidl b/core/java/android/companion/AssociationInfo.aidl
similarity index 95%
rename from core/java/android/companion/Association.aidl
rename to core/java/android/companion/AssociationInfo.aidl
index 2a28f1f..abab743 100644
--- a/core/java/android/companion/Association.aidl
+++ b/core/java/android/companion/AssociationInfo.aidl
@@ -15,4 +15,4 @@
*/
package android.companion;
-parcelable Association;
+parcelable AssociationInfo;
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/AssociationInfo.java
similarity index 91%
rename from core/java/android/companion/Association.java
rename to core/java/android/companion/AssociationInfo.java
index 7cea33d..ab1eb1f 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -39,7 +39,7 @@
* TODO(b/1979395): un-hide and rename to AssociationInfo when implementing public APIs that use
* this class.
*/
-public final class Association implements Parcelable {
+public final class AssociationInfo implements Parcelable {
/**
* A unique ID of this Association record.
* Disclosed to the clients (ie. companion applications) for referring to this record (eg. in
@@ -63,7 +63,7 @@
*
* @hide
*/
- public Association(int associationId, @UserIdInt int userId, @NonNull String packageName,
+ public AssociationInfo(int associationId, @UserIdInt int userId, @NonNull String packageName,
@NonNull List<DeviceId> deviceIds, @Nullable String deviceProfile,
boolean managedByCompanionApp, boolean notifyOnDeviceNearby, long timeApprovedMs) {
if (associationId <= 0) {
@@ -178,8 +178,8 @@
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (!(o instanceof Association)) return false;
- final Association that = (Association) o;
+ if (!(o instanceof AssociationInfo)) return false;
+ final AssociationInfo that = (AssociationInfo) o;
return mAssociationId == that.mAssociationId
&& mUserId == that.mUserId
&& mManagedByCompanionApp == that.mManagedByCompanionApp
@@ -216,7 +216,7 @@
dest.writeLong(mTimeApprovedMs);
}
- private Association(@NonNull Parcel in) {
+ private AssociationInfo(@NonNull Parcel in) {
mAssociationId = in.readInt();
mUserId = in.readInt();
@@ -230,16 +230,16 @@
mTimeApprovedMs = in.readLong();
}
- public static final Parcelable.Creator<Association> CREATOR =
- new Parcelable.Creator<Association>() {
+ public static final Parcelable.Creator<AssociationInfo> CREATOR =
+ new Parcelable.Creator<AssociationInfo>() {
@Override
- public Association[] newArray(int size) {
- return new Association[size];
+ public AssociationInfo[] newArray(int size) {
+ return new AssociationInfo[size];
}
@Override
- public Association createFromParcel(@NonNull Parcel in) {
- return new Association(in);
+ public AssociationInfo createFromParcel(@NonNull Parcel in) {
+ return new AssociationInfo(in);
}
};
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index d42d6b4..a8fe602 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -319,13 +319,13 @@
}
/**
- * Gets all package-device {@link Association}s for the current user.
+ * Gets all package-device {@link AssociationInfo}s for the current user.
*
* @return the associations list
* @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
- public @NonNull List<Association> getAllAssociations() {
+ public @NonNull List<AssociationInfo> getAllAssociations() {
if (!checkFeaturePresent()) {
return Collections.emptyList();
}
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 25c5cb3..101f948 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -18,7 +18,7 @@
import android.app.PendingIntent;
import android.companion.IFindDeviceCallback;
-import android.companion.Association;
+import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.content.ComponentName;
@@ -36,7 +36,7 @@
in String callingPackage);
List<String> getAssociations(String callingPackage, int userId);
- List<Association> getAssociationsForUser(int userId);
+ List<AssociationInfo> getAssociationsForUser(int userId);
void disassociate(String deviceMacAddress, String callingPackage);
diff --git a/core/java/android/companion/Association.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
similarity index 74%
copy from core/java/android/companion/Association.aidl
copy to core/java/android/companion/virtual/IVirtualDevice.aidl
index 2a28f1f..0aa442b 100644
--- a/core/java/android/companion/Association.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -13,6 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package android.companion.virtual;
+
+/**
+ * Interface for a virtual device.
+ *
+ * @hide
+ */
+interface IVirtualDevice {
+ void close();
+}
diff --git a/core/java/android/companion/Association.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
similarity index 62%
copy from core/java/android/companion/Association.aidl
copy to core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index 2a28f1f..91e717d 100644
--- a/core/java/android/companion/Association.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -13,6 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package android.companion.virtual;
+
+import android.companion.virtual.IVirtualDevice;
+
+/**
+ * Interface for communication between VirtualDeviceManager and VirtualDeviceManagerService.
+ *
+ * @hide
+ */
+interface IVirtualDeviceManager {
+
+ IVirtualDevice createVirtualDevice();
+}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
new file mode 100644
index 0000000..6187de5
--- /dev/null
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+
+/**
+ * System level service for managing virtual devices.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.VIRTUAL_DEVICE_SERVICE)
+public final class VirtualDeviceManager {
+
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = "VirtualDeviceManager";
+
+ private final IVirtualDeviceManager mService;
+ private final Context mContext;
+
+ /** @hide */
+ public VirtualDeviceManager(
+ @Nullable IVirtualDeviceManager service, @NonNull Context context) {
+ mService = service;
+ mContext = context;
+ }
+
+ /**
+ * Creates a virtual device.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @Nullable
+ public VirtualDevice createVirtualDevice() {
+ // TODO(b/194949534): Add CDM association ID here and unhide this API
+ try {
+ IVirtualDevice virtualDevice = mService.createVirtualDevice();
+ return new VirtualDevice(mContext, virtualDevice);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * A virtual device has its own virtual display, audio output, microphone, and camera etc. The
+ * creator of a virtual device can take the output from the virtual display and stream it over
+ * to another device, and inject input events that are received from the remote device.
+ */
+ public static class VirtualDevice implements AutoCloseable {
+
+ private final Context mContext;
+ private final IVirtualDevice mVirtualDevice;
+
+ private VirtualDevice(Context context, IVirtualDevice virtualDevice) {
+ mContext = context.getApplicationContext();
+ mVirtualDevice = virtualDevice;
+ }
+
+ /**
+ * Closes the virtual device, stopping and tearing down any virtual displays,
+ * audio policies, and event injection that's currently in progress.
+ */
+ public void close() {
+ try {
+ mVirtualDevice.close();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index cfd3417..bfc4333 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3799,6 +3799,7 @@
//@hide: INCIDENT_COMPANION_SERVICE,
//@hide: STATS_COMPANION_SERVICE,
COMPANION_DEVICE_SERVICE,
+ //@hide: VIRTUAL_DEVICE_SERVICE,
CROSS_PROFILE_APPS_SERVICE,
//@hide: SYSTEM_UPDATE_SERVICE,
//@hide: TIME_DETECTOR_SERVICE,
@@ -5262,6 +5263,16 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.companion.virtual.VirtualDeviceManager} for managing virtual devices.
+ *
+ * @see #getSystemService(String)
+ * @see android.companion.virtual.VirtualDeviceManager
+ * @hide
+ */
+ public static final String VIRTUAL_DEVICE_SERVICE = "virtualdevice";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.ConsumerIrManager} for transmitting infrared
* signals from the device.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6446b44..e838d93 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -32,6 +32,7 @@
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.app.AppGlobals;
+import android.bluetooth.BluetoothDevice;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -11688,6 +11689,16 @@
if (fromProtectedComponent) {
mLocalFlags |= LOCAL_FLAG_FROM_PROTECTED_COMPONENT;
}
+
+ // Special attribution fix-up logic for any BluetoothDevice extras
+ // passed via Bluetooth intents
+ if (mAction != null && mAction.startsWith("android.bluetooth.")
+ && hasExtra(BluetoothDevice.EXTRA_DEVICE)) {
+ final BluetoothDevice device = getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (device != null) {
+ device.prepareToEnterProcess(source);
+ }
+ }
}
/** @hide */
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index c7f92c9..660368a 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -2,11 +2,9 @@
per-file Context.java = *
per-file ContextWrapper.java = *
per-file Content* = file:/services/core/java/com/android/server/am/OWNERS
-per-file IntentFilter.java = toddke@google.com
-per-file IntentFilter.java = patb@google.com
+per-file IntentFilter.java = file:/PACKAGE_MANAGER_OWNERS
per-file IntentFilter.java = file:/services/core/java/com/android/server/am/OWNERS
-per-file Intent.java = toddke@google.com
-per-file Intent.java = patb@google.com
+per-file Intent.java = file:/PACKAGE_MANAGER_OWNERS
per-file Intent.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file Intent.java = file:/services/core/java/com/android/server/am/OWNERS
per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 172a51a..cbb5c2b 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -995,9 +995,10 @@
* OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
* OVERRIDE_MIN_ASPECT_RATIO_LARGE
*
- * If OVERRIDE_MIN_ASPECT_RATIO is applied, the min aspect ratio given in the app's
- * manifest will be overridden to the largest enabled aspect ratio treatment unless the app's
- * manifest value is higher.
+ * If OVERRIDE_MIN_ASPECT_RATIO is applied, and the activity's orientation is fixed to
+ * portrait, the min aspect ratio given in the app's manifest will be overridden to the
+ * largest enabled aspect ratio treatment unless the app's manifest value is higher.
+ * TODO(b/203647190): add OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY instead of portrait by default
* @hide
*/
@ChangeId
@@ -1233,8 +1234,8 @@
* Returns true if the activity has maximum or minimum aspect ratio.
* @hide
*/
- public boolean hasFixedAspectRatio() {
- return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
+ public boolean hasFixedAspectRatio(@ScreenOrientation int orientation) {
+ return getMaxAspectRatio() != 0 || getMinAspectRatio(orientation) != 0;
}
/**
@@ -1393,10 +1394,14 @@
* {@code getManifestMinAspectRatio}.
* @hide
*/
- public float getMinAspectRatio() {
+ public float getMinAspectRatio(@ScreenOrientation int orientation) {
+ // TODO(b/203647190): check orientation only if OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY
+ // In case the activity's orientation isn't fixed to portrait, OVERRIDE_MIN_ASPECT_RATIO
+ // shouldn't be applied.
if (applicationInfo == null || !CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO,
applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ UserHandle.getUserHandleForUid(applicationInfo.uid))
+ || !isFixedOrientationPortrait(orientation)) {
return mMinAspectRatio;
}
@@ -1522,9 +1527,10 @@
if (getMaxAspectRatio() != 0) {
pw.println(prefix + "maxAspectRatio=" + getMaxAspectRatio());
}
- if (getMinAspectRatio() != 0) {
- pw.println(prefix + "minAspectRatio=" + getMinAspectRatio());
- if (getManifestMinAspectRatio() != getMinAspectRatio()) {
+ final float minAspectRatio = getMinAspectRatio(screenOrientation);
+ if (minAspectRatio != 0) {
+ pw.println(prefix + "minAspectRatio=" + minAspectRatio);
+ if (getManifestMinAspectRatio() != minAspectRatio) {
pw.println(prefix + "getManifestMinAspectRatio=" + getManifestMinAspectRatio());
}
}
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 0d8fdcc..cc4782a 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -1,6 +1,15 @@
{
"imports": [
{
+ "path": "frameworks/base/services/tests/PackageManagerServiceTests"
+ },
+ {
+ "path": "frameworks/base/services/tests/PackageManager"
+ },
+ {
+ "path": "frameworks/base/services/tests/PackageManagerComponentOverrideTests"
+ },
+ {
"path": "cts/tests/tests/packageinstaller"
},
{
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index e93ec00..dd00c3a 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -21,6 +21,8 @@
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.system.ErrnoException;
+import android.system.Os;
import java.io.Closeable;
import java.io.FileDescriptor;
@@ -40,7 +42,7 @@
* the data extends to the end of the file.
*/
public static final long UNKNOWN_LENGTH = -1;
-
+
@UnsupportedAppUsage
private final ParcelFileDescriptor mFd;
@UnsupportedAppUsage
@@ -52,11 +54,11 @@
/**
* Create a new AssetFileDescriptor from the given values.
*
- * @param fd The underlying file descriptor.
+ * @param fd The underlying file descriptor.
* @param startOffset The location within the file that the asset starts.
- * This must be 0 if length is UNKNOWN_LENGTH.
- * @param length The number of bytes of the asset, or
- * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
+ * This must be 0 if length is UNKNOWN_LENGTH.
+ * @param length The number of bytes of the asset, or
+ * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
*/
public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
long length) {
@@ -66,13 +68,13 @@
/**
* Create a new AssetFileDescriptor from the given values.
*
- * @param fd The underlying file descriptor.
+ * @param fd The underlying file descriptor.
* @param startOffset The location within the file that the asset starts.
- * This must be 0 if length is UNKNOWN_LENGTH.
- * @param length The number of bytes of the asset, or
- * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
- * @param extras additional details that can be used to interpret the
- * underlying file descriptor. May be null.
+ * This must be 0 if length is UNKNOWN_LENGTH.
+ * @param length The number of bytes of the asset, or
+ * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
+ * @param extras additional details that can be used to interpret the
+ * underlying file descriptor. May be null.
*/
public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
long length, Bundle extras) {
@@ -97,7 +99,7 @@
public ParcelFileDescriptor getParcelFileDescriptor() {
return mFd;
}
-
+
/**
* Returns the FileDescriptor that can be used to read the data in the
* file.
@@ -105,7 +107,7 @@
public FileDescriptor getFileDescriptor() {
return mFd.getFileDescriptor();
}
-
+
/**
* Returns the byte offset where this asset entry's data starts.
*/
@@ -129,7 +131,7 @@
* ParcelFileDescriptor.getStatSize()} to find the total size of the file,
* returning that number if found or {@link #UNKNOWN_LENGTH} if it could
* not be determined.
- *
+ *
* @see #getDeclaredLength()
*/
public long getLength() {
@@ -139,19 +141,19 @@
long len = mFd.getStatSize();
return len >= 0 ? len : UNKNOWN_LENGTH;
}
-
+
/**
* Return the actual number of bytes that were declared when the
* AssetFileDescriptor was constructed. Will be
* {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data
* should be read to the end of the file.
- *
+ *
* @see #getDeclaredLength()
*/
public long getDeclaredLength() {
return mLength;
}
-
+
/**
* Convenience for calling <code>getParcelFileDescriptor().close()</code>.
*/
@@ -174,7 +176,7 @@
}
return new AutoCloseInputStream(this);
}
-
+
/**
* Create and return a new auto-close output stream for this asset. This
* will either return a full asset {@link AutoCloseOutputStream}, or
@@ -189,13 +191,13 @@
}
return new AutoCloseOutputStream(this);
}
-
+
@Override
public String toString() {
return "{AssetFileDescriptor: " + mFd
+ " start=" + mStartOffset + " len=" + mLength + "}";
}
-
+
/**
* An InputStream you can create on a ParcelFileDescriptor, which will
* take care of calling {@link ParcelFileDescriptor#close
@@ -203,19 +205,24 @@
*/
public static class AutoCloseInputStream
extends ParcelFileDescriptor.AutoCloseInputStream {
- private long mRemaining;
-
+ private final long mSizeFromStartOffset;
+ private final long mStartOffset;
+ private long mPosFromStartOffset;
+
public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
super(fd.getParcelFileDescriptor());
+ // this skip is necessary if getChannel() is called
super.skip(fd.getStartOffset());
- mRemaining = (int)fd.getLength();
+ mSizeFromStartOffset = fd.getLength();
+ mStartOffset = fd.getStartOffset();
}
@Override
public int available() throws IOException {
- return mRemaining >= 0
- ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff)
- : super.available();
+ long available = mSizeFromStartOffset - mPosFromStartOffset;
+ return available >= 0
+ ? (available < 0x7fffffff ? (int) available : 0x7fffffff)
+ : 0;
}
@Override
@@ -227,15 +234,24 @@
@Override
public int read(byte[] buffer, int offset, int count) throws IOException {
- if (mRemaining >= 0) {
- if (mRemaining == 0) return -1;
- if (count > mRemaining) count = (int)mRemaining;
- int res = super.read(buffer, offset, count);
- if (res >= 0) mRemaining -= res;
- return res;
+ int available = available();
+
+ if (available <= 0) {
+ return -1;
+ } else {
+ if (count > available) count = available;
+ try {
+ int res = Os.pread(getFD(), buffer, offset, count,
+ mStartOffset + mPosFromStartOffset);
+ // pread returns 0 at end of file, while java's InputStream interface
+ // requires -1
+ if (res == 0) res = -1;
+ if (res >= 0) mPosFromStartOffset += res;
+ return res;
+ } catch (ErrnoException e) {
+ throw new IOException(e);
+ }
}
-
- return super.read(buffer, offset, count);
}
@Override
@@ -245,41 +261,31 @@
@Override
public long skip(long count) throws IOException {
- if (mRemaining >= 0) {
- if (mRemaining == 0) return -1;
- if (count > mRemaining) count = mRemaining;
- long res = super.skip(count);
- if (res >= 0) mRemaining -= res;
- return res;
+ int available = available();
+ if (available <= 0) {
+ return -1;
+ } else {
+ if (count > available) count = available;
+ mPosFromStartOffset += count;
+ return count;
}
-
- return super.skip(count);
}
@Override
public void mark(int readlimit) {
- if (mRemaining >= 0) {
- // Not supported.
- return;
- }
- super.mark(readlimit);
+ // Not supported.
+ return;
}
@Override
public boolean markSupported() {
- if (mRemaining >= 0) {
- return false;
- }
- return super.markSupported();
+ return false;
}
@Override
public synchronized void reset() throws IOException {
- if (mRemaining >= 0) {
- // Not supported.
- return;
- }
- super.reset();
+ // Not supported.
+ return;
}
}
@@ -291,25 +297,25 @@
public static class AutoCloseOutputStream
extends ParcelFileDescriptor.AutoCloseOutputStream {
private long mRemaining;
-
+
public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException {
super(fd.getParcelFileDescriptor());
if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) {
throw new IOException("Unable to seek");
}
- mRemaining = (int)fd.getLength();
+ mRemaining = (int) fd.getLength();
}
@Override
public void write(byte[] buffer, int offset, int count) throws IOException {
if (mRemaining >= 0) {
if (mRemaining == 0) return;
- if (count > mRemaining) count = (int)mRemaining;
+ if (count > mRemaining) count = (int) mRemaining;
super.write(buffer, offset, count);
mRemaining -= count;
return;
}
-
+
super.write(buffer, offset, count);
}
@@ -318,12 +324,12 @@
if (mRemaining >= 0) {
if (mRemaining == 0) return;
int count = buffer.length;
- if (count > mRemaining) count = (int)mRemaining;
+ if (count > mRemaining) count = (int) mRemaining;
super.write(buffer);
mRemaining -= count;
return;
}
-
+
super.write(buffer);
}
@@ -335,7 +341,7 @@
mRemaining--;
return;
}
-
+
super.write(oneByte);
}
}
@@ -375,6 +381,7 @@
public AssetFileDescriptor createFromParcel(Parcel in) {
return new AssetFileDescriptor(in);
}
+
public AssetFileDescriptor[] newArray(int size) {
return new AssetFileDescriptor[size];
}
diff --git a/core/java/android/hardware/SensorDirectChannel.java b/core/java/android/hardware/SensorDirectChannel.java
index 214d3ec..293d8b0 100644
--- a/core/java/android/hardware/SensorDirectChannel.java
+++ b/core/java/android/hardware/SensorDirectChannel.java
@@ -64,7 +64,7 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, prefix = { "RATE_" }, value = {
+ @IntDef(prefix = { "RATE_" }, value = {
RATE_STOP,
RATE_NORMAL,
RATE_FAST,
diff --git a/core/java/android/hardware/biometrics/BiometricOverlayConstants.java b/core/java/android/hardware/biometrics/BiometricOverlayConstants.java
index 603b06d..065ae64a 100644
--- a/core/java/android/hardware/biometrics/BiometricOverlayConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricOverlayConstants.java
@@ -38,13 +38,16 @@
int REASON_AUTH_KEYGUARD = 4;
/** Non-specific usage (from FingerprintManager). */
int REASON_AUTH_OTHER = 5;
+ /** Usage from Settings. */
+ int REASON_AUTH_SETTINGS = 6;
@IntDef({REASON_UNKNOWN,
REASON_ENROLL_FIND_SENSOR,
REASON_ENROLL_ENROLLING,
REASON_AUTH_BP,
REASON_AUTH_KEYGUARD,
- REASON_AUTH_OTHER})
+ REASON_AUTH_OTHER,
+ REASON_AUTH_SETTINGS})
@Retention(RetentionPolicy.SOURCE)
@interface ShowReason {}
}
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 1d07a03..eb8f43e 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -220,8 +220,10 @@
String networkId = null;
boolean roaming = !snapshot.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
- boolean metered = !snapshot.getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ boolean metered = !(snapshot.getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ || snapshot.getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED));
final int oemManaged = getOemBitfield(snapshot.getNetworkCapabilities());
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 08f75df..74506da 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -160,19 +160,19 @@
}
/**
- * Template to match cellular networks with the given IMSI and {@code ratType}.
- * Use {@link #NETWORK_TYPE_ALL} to include all network types when filtering.
- * See {@code TelephonyManager.NETWORK_TYPE_*}.
+ * Template to match cellular networks with the given IMSI, {@code ratType} and
+ * {@code metered}. Use {@link #NETWORK_TYPE_ALL} to include all network types when
+ * filtering. See {@code TelephonyManager.NETWORK_TYPE_*}.
*/
public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
- @NetworkType int ratType) {
+ @NetworkType int ratType, int metered) {
if (TextUtils.isEmpty(subscriberId)) {
return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null,
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
+ metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null,
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
+ metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
@@ -305,6 +305,7 @@
}
}
+ // TODO: Deprecate this constructor, mark it @UnsupportedAppUsage(maxTargetSdk = S)
@UnsupportedAppUsage
public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
@@ -312,9 +313,14 @@
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
String networkId) {
- this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
- SUBSCRIBER_ID_MATCH_RULE_EXACT);
+ // Older versions used to only match MATCH_MOBILE and MATCH_MOBILE_WILDCARD templates
+ // to metered networks. It is now possible to match mobile with any meteredness, but
+ // in order to preserve backward compatibility of @UnsupportedAppUsage methods, this
+ //constructor passes METERED_YES for these types.
+ this(matchRule, subscriberId, matchSubscriberIds, networkId,
+ (matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD) ? METERED_YES
+ : METERED_ALL , ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
// TODO: Remove it after updating all of the caller.
@@ -589,11 +595,7 @@
// TODO: consider matching against WiMAX subscriber identity
return true;
} else {
- // Only metered mobile network would be matched regardless of metered filter.
- // This is used to exclude non-metered APNs, e.g. IMS. See ag/908650.
- // TODO: Respect metered filter and remove mMetered condition.
- return (ident.mType == TYPE_MOBILE && ident.mMetered)
- && !ArrayUtils.isEmpty(mMatchSubscriberIds)
+ return ident.mType == TYPE_MOBILE && !ArrayUtils.isEmpty(mMatchSubscriberIds)
&& ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId)
&& matchesCollapsedRatType(ident);
}
@@ -707,8 +709,7 @@
if (ident.mType == TYPE_WIMAX) {
return true;
} else {
- return (ident.mType == TYPE_MOBILE && ident.mMetered)
- && matchesCollapsedRatType(ident);
+ return ident.mType == TYPE_MOBILE && matchesCollapsedRatType(ident);
}
}
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 1c1fc2c..1853c65 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -174,7 +174,7 @@
public static final int PROCESS_STATE_BACKGROUND = 2;
public static final int PROCESS_STATE_FOREGROUND_SERVICE = 3;
- static final int PROCESS_STATE_COUNT = 4;
+ public static final int PROCESS_STATE_COUNT = 4;
private static final String[] sProcessStateNames = new String[PROCESS_STATE_COUNT];
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 30613b9..da968b3 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -654,6 +654,24 @@
}
/**
+ * Maps BatteryStats.Uid process state to the BatteryConsumer process state.
+ */
+ public static @BatteryConsumer.ProcessState int
+ mapUidProcessStateToBatteryConsumerProcessState(int processState) {
+ switch (processState) {
+ case BatteryStats.Uid.PROCESS_STATE_TOP:
+ return BatteryConsumer.PROCESS_STATE_FOREGROUND;
+ case BatteryStats.Uid.PROCESS_STATE_BACKGROUND:
+ case BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING:
+ return BatteryConsumer.PROCESS_STATE_BACKGROUND;
+ case BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE:
+ return BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+ default:
+ return BatteryConsumer.PROCESS_STATE_ANY;
+ }
+ }
+
+ /**
* Returns true if battery consumption is tracked on a per-process-state basis.
*/
public abstract boolean isProcessStateDataAvailable();
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 635f581..b677b69 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -313,7 +313,7 @@
* @hide
*/
@CriticalNative
- public static final native boolean isHandlingTransaction();
+ public static final native boolean isDirectlyHandlingTransaction();
/**
* Return the Linux uid assigned to the process that sent the transaction
@@ -323,7 +323,7 @@
* executing an incoming transaction.
*/
public static final int getCallingUidOrThrow() {
- if (!isHandlingTransaction()) {
+ if (!isDirectlyHandlingTransaction()) {
throw new IllegalStateException(
"Thread is not in a binder transcation");
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 394d270..743468a 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1024,7 +1024,7 @@
* will also enable {@link StrictMode.ThreadPolicy.Builder#detectUnbufferedIo}.</li>
* <li>{@link android.provider.DocumentsContract}'s various methods will throw failure
* exceptions back to the caller instead of returning null.
- * <li>{@link View#hasFocusable View.hasFocusable} now includes auto-focusable views.</li>
+ * <li>{@link View#hasFocusable() View.hasFocusable} now includes auto-focusable views.</li>
* <li>{@link android.view.SurfaceView} will no longer always change the underlying
* Surface object when something about it changes; apps need to look at the current
* state of the object to determine which things they are interested in have changed.</li>
@@ -1132,6 +1132,13 @@
public static final int S = 31;
/**
+ * S V2.
+ *
+ * Once more unto the breach, dear friends, once more.
+ */
+ public static final int S_V2 = 32;
+
+ /**
* Tiramisu.
*/
public static final int TIRAMISU = CUR_DEVELOPMENT;
diff --git a/core/java/android/os/BytesMatcher.java b/core/java/android/os/BytesMatcher.java
deleted file mode 100644
index 8974c5e..0000000
--- a/core/java/android/os/BytesMatcher.java
+++ /dev/null
@@ -1,315 +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.os;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.bluetooth.BluetoothUuid;
-import android.net.MacAddress;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.util.HexDump;
-
-import java.util.ArrayList;
-import java.util.function.Predicate;
-
-/**
- * Predicate that tests if a given {@code byte[]} value matches a set of
- * configured rules.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader rule
- * might accept that same value, or vice versa.
- * <p>
- * Matchers can contain rules of varying lengths, and tested values will only be
- * matched against rules of the exact same length. This is designed to support
- * {@link BluetoothUuid} style values which can be variable length.
- *
- * @hide
- */
-public class BytesMatcher implements Predicate<byte[]> {
- private static final String TAG = "BytesMatcher";
-
- private static final char TYPE_EXACT_ACCEPT = '+';
- private static final char TYPE_EXACT_REJECT = '-';
- private static final char TYPE_PREFIX_ACCEPT = '⊆';
- private static final char TYPE_PREFIX_REJECT = '⊈';
-
- private final ArrayList<Rule> mRules = new ArrayList<>();
-
- private static class Rule {
- public final char type;
- public final @NonNull byte[] value;
- public final @Nullable byte[] mask;
-
- public Rule(char type, @NonNull byte[] value, @Nullable byte[] mask) {
- if (mask != null && value.length != mask.length) {
- throw new IllegalArgumentException(
- "Expected length " + value.length + " but found " + mask.length);
- }
- this.type = type;
- this.value = value;
- this.mask = mask;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- encode(builder);
- return builder.toString();
- }
-
- public void encode(@NonNull StringBuilder builder) {
- builder.append(type);
- builder.append(HexDump.toHexString(value));
- if (mask != null) {
- builder.append('/');
- builder.append(HexDump.toHexString(mask));
- }
- }
-
- public boolean test(@NonNull byte[] value) {
- switch (type) {
- case TYPE_EXACT_ACCEPT:
- case TYPE_EXACT_REJECT:
- if (value.length != this.value.length) {
- return false;
- }
- break;
- case TYPE_PREFIX_ACCEPT:
- case TYPE_PREFIX_REJECT:
- if (value.length < this.value.length) {
- return false;
- }
- break;
- }
- for (int i = 0; i < this.value.length; i++) {
- byte local = this.value[i];
- byte remote = value[i];
- if (this.mask != null) {
- local &= this.mask[i];
- remote &= this.mask[i];
- }
- if (local != remote) {
- return false;
- }
- }
- return true;
- }
- }
-
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code true} when a value being tested matches it. This rule will only
- * match values of the exact same length.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addExactAcceptRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_EXACT_ACCEPT, value, mask));
- }
-
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code false} when a value being tested matches it. This rule will only
- * match values of the exact same length.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addExactRejectRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_EXACT_REJECT, value, mask));
- }
-
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code true} when a value being tested matches it. This rule will match
- * values of the exact same length or longer.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addPrefixAcceptRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_PREFIX_ACCEPT, value, mask));
- }
-
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code false} when a value being tested matches it. This rule will match
- * values of the exact same length or longer.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addPrefixRejectRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_PREFIX_REJECT, value, mask));
- }
-
- /**
- * Test if the given {@code ParcelUuid} value matches the set of rules
- * configured in this matcher.
- */
- public boolean testBluetoothUuid(@NonNull ParcelUuid value) {
- return test(BluetoothUuid.uuidToBytes(value));
- }
-
- /**
- * Test if the given {@code MacAddress} value matches the set of rules
- * configured in this matcher.
- */
- public boolean testMacAddress(@NonNull MacAddress value) {
- return test(value.toByteArray());
- }
-
- /**
- * Test if the given {@code byte[]} value matches the set of rules
- * configured in this matcher.
- */
- @Override
- public boolean test(@NonNull byte[] value) {
- return test(value, false);
- }
-
- /**
- * Test if the given {@code byte[]} value matches the set of rules
- * configured in this matcher.
- */
- public boolean test(@NonNull byte[] value, boolean defaultValue) {
- final int size = mRules.size();
- for (int i = 0; i < size; i++) {
- final Rule rule = mRules.get(i);
- if (rule.test(value)) {
- switch (rule.type) {
- case TYPE_EXACT_ACCEPT:
- case TYPE_PREFIX_ACCEPT:
- return true;
- case TYPE_EXACT_REJECT:
- case TYPE_PREFIX_REJECT:
- return false;
- }
- }
- }
- return defaultValue;
- }
-
- /**
- * Encode the given matcher into a human-readable {@link String} which can
- * be used to transport matchers across device boundaries.
- * <p>
- * The human-readable format is an ordered list separated by commas, where
- * each rule is a {@code +} or {@code -} symbol indicating if the match
- * should be accepted or rejected, then followed by a hex value and an
- * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
- * encoded matcher.
- *
- * @see #decode(String)
- */
- public static @NonNull String encode(@NonNull BytesMatcher matcher) {
- final StringBuilder builder = new StringBuilder();
- final int size = matcher.mRules.size();
- for (int i = 0; i < size; i++) {
- final Rule rule = matcher.mRules.get(i);
- rule.encode(builder);
- builder.append(',');
- }
- if (builder.length() > 0) {
- builder.deleteCharAt(builder.length() - 1);
- }
- return builder.toString();
- }
-
- /**
- * Decode the given human-readable {@link String} used to transport matchers
- * across device boundaries.
- * <p>
- * The human-readable format is an ordered list separated by commas, where
- * each rule is a {@code +} or {@code -} symbol indicating if the match
- * should be accepted or rejected, then followed by a hex value and an
- * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
- * encoded matcher.
- *
- * @see #encode(BytesMatcher)
- */
- public static @NonNull BytesMatcher decode(@Nullable String value) {
- final BytesMatcher matcher = new BytesMatcher();
- if (TextUtils.isEmpty(value)) return matcher;
-
- final int length = value.length();
- for (int i = 0; i < length;) {
- final char type = value.charAt(i);
-
- int nextRule = value.indexOf(',', i);
- int nextMask = value.indexOf('/', i);
-
- if (nextRule == -1) nextRule = length;
- if (nextMask > nextRule) nextMask = -1;
-
- final byte[] ruleValue;
- final byte[] ruleMask;
- if (nextMask >= 0) {
- ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextMask));
- ruleMask = HexDump.hexStringToByteArray(value.substring(nextMask + 1, nextRule));
- } else {
- ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextRule));
- ruleMask = null;
- }
-
- switch (type) {
- case TYPE_EXACT_ACCEPT:
- matcher.addExactAcceptRule(ruleValue, ruleMask);
- break;
- case TYPE_EXACT_REJECT:
- matcher.addExactRejectRule(ruleValue, ruleMask);
- break;
- case TYPE_PREFIX_ACCEPT:
- matcher.addPrefixAcceptRule(ruleValue, ruleMask);
- break;
- case TYPE_PREFIX_REJECT:
- matcher.addPrefixRejectRule(ruleValue, ruleMask);
- break;
- default:
- Log.w(TAG, "Ignoring unknown type " + type);
- break;
- }
-
- i = nextRule + 1;
- }
- return matcher;
- }
-}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index ab2c8c0..1468d9f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -3094,14 +3095,24 @@
* Parcelables.
*/
@Nullable
- public final ArrayList readArrayList(@Nullable ClassLoader loader) {
- int N = readInt();
- if (N < 0) {
- return null;
- }
- ArrayList l = new ArrayList(N);
- readListInternal(l, N, loader, /* clazz */ null);
- return l;
+ public ArrayList readArrayList(@Nullable ClassLoader loader) {
+ return readArrayListInternal(loader, /* clazz */ null);
+ }
+
+ /**
+ * Same as {@link #readArrayList(ClassLoader)} but accepts {@code clazz} parameter as
+ * the type required for each item.
+ *
+ * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+ * is not an instance of that class or any of its children classes or there was an error
+ * trying to instantiate an element.
+ */
+ @SuppressLint({"ConcreteCollection", "NullableCollection"})
+ @Nullable
+ public <T> ArrayList<T> readArrayList(@Nullable ClassLoader loader,
+ @NonNull Class<? extends T> clazz) {
+ Objects.requireNonNull(clazz);
+ return readArrayListInternal(loader, clazz);
}
/**
@@ -3111,14 +3122,23 @@
* Parcelables.
*/
@Nullable
- public final Object[] readArray(@Nullable ClassLoader loader) {
- int N = readInt();
- if (N < 0) {
- return null;
- }
- Object[] l = new Object[N];
- readArrayInternal(l, N, loader);
- return l;
+ public Object[] readArray(@Nullable ClassLoader loader) {
+ return readArrayInternal(loader, /* clazz */ null);
+ }
+
+ /**
+ * Same as {@link #readArray(ClassLoader)} but accepts {@code clazz} parameter as
+ * the type required for each item.
+ *
+ * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+ * is not an instance of that class or any of its children classes or there was an error
+ * trying to instantiate an element.
+ */
+ @SuppressLint({"ArrayReturn", "NullableCollection"})
+ @Nullable
+ public <T> T[] readArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ Objects.requireNonNull(clazz);
+ return readArrayInternal(loader, clazz);
}
/**
@@ -3128,14 +3148,23 @@
* Parcelables.
*/
@Nullable
- public final <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
- int N = readInt();
- if (N < 0) {
- return null;
- }
- SparseArray sa = new SparseArray(N);
- readSparseArrayInternal(sa, N, loader);
- return sa;
+ public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
+ return readSparseArrayInternal(loader, /* clazz */ null);
+ }
+
+ /**
+ * Same as {@link #readSparseArray(ClassLoader)} but accepts {@code clazz} parameter as
+ * the type required for each item.
+ *
+ * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+ * is not an instance of that class or any of its children classes or there was an error
+ * trying to instantiate an element.
+ */
+ @Nullable
+ public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader,
+ @NonNull Class<? extends T> clazz) {
+ Objects.requireNonNull(clazz);
+ return readSparseArrayInternal(loader, clazz);
}
/**
@@ -3851,7 +3880,7 @@
"Parcel " + this + ": Unmarshalling unknown type code " + type
+ " at offset " + off);
}
- if (clazz != null && !clazz.isInstance(object)) {
+ if (object != null && clazz != null && !clazz.isInstance(object)) {
throw new BadParcelableException("Unparcelled object " + object
+ " is not an instance of required class " + clazz.getName()
+ " provided in the parameter");
@@ -3910,7 +3939,6 @@
}
/**
- *
* @param clazz The type of the parcelable expected or {@code null} for performing no checks.
*/
@SuppressWarnings("unchecked")
@@ -3969,7 +3997,7 @@
* as the required type.
*
* @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
- * is not an instance of that class or any of its children class or there there was an error
+ * is not an instance of that class or any of its children classes or there there was an error
* trying to read the {@link Parcelable.Creator}.
*/
@Nullable
@@ -4092,17 +4120,25 @@
return p;
}
- /** @hide */
+ /**
+ * Same as {@link #readParcelableArray(ClassLoader)} but accepts {@code clazz} parameter as
+ * the type required for each item.
+ *
+ * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+ * is not an instance of that class or any of its children classes or there was an error
+ * trying to instantiate an element.
+ */
+ @SuppressLint({"ArrayReturn", "NullableCollection"})
+ @SuppressWarnings("unchecked")
@Nullable
- public final <T extends Parcelable> T[] readParcelableArray(@Nullable ClassLoader loader,
- @NonNull Class<T> clazz) {
- int N = readInt();
- if (N < 0) {
+ public <T> T[] readParcelableArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ int n = readInt();
+ if (n < 0) {
return null;
}
- T[] p = (T[]) Array.newInstance(clazz, N);
- for (int i = 0; i < N; i++) {
- p[i] = readParcelable(loader);
+ T[] p = (T[]) Array.newInstance(clazz, n);
+ for (int i = 0; i < n; i++) {
+ p[i] = readParcelableInternal(loader, clazz);
}
return p;
}
@@ -4320,9 +4356,12 @@
return result;
}
- private void readListInternal(@NonNull List outVal, int n,
- @Nullable ClassLoader loader) {
- readListInternal(outVal, n, loader, null);
+ /**
+ * The method is replaced by {@link #readListInternal(List, int, ClassLoader, Class)}, however
+ * we are keeping this unused method here to allow unsupported app usages.
+ */
+ private void readListInternal(@NonNull List outVal, int n, @Nullable ClassLoader loader) {
+ readListInternal(outVal, n, loader, /* clazz */ null);
}
/**
@@ -4338,26 +4377,88 @@
}
}
+ /**
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
+ */
+ @SuppressLint({"ConcreteCollection", "NullableCollection"})
+ @Nullable
+ private <T> ArrayList<T> readArrayListInternal(@Nullable ClassLoader loader,
+ @Nullable Class<? extends T> clazz) {
+ int n = readInt();
+ if (n < 0) {
+ return null;
+ }
+ ArrayList<T> l = new ArrayList<>(n);
+ readListInternal(l, n, loader, clazz);
+ return l;
+ }
+
+ /**
+ * The method is replaced by {@link #readArrayInternal(ClassLoader, Class)}, however
+ * we are keeping this unused method here to allow unsupported app usages.
+ */
private void readArrayInternal(@NonNull Object[] outVal, int N,
@Nullable ClassLoader loader) {
for (int i = 0; i < N; i++) {
- Object value = readValue(loader);
- //Log.d(TAG, "Unmarshalling value=" + value);
+ Object value = readValue(loader, /* clazz */ null);
outVal[i] = value;
}
}
+ /**
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
+ */
+ @SuppressWarnings("unchecked")
+ @Nullable
+ private <T> T[] readArrayInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+ int n = readInt();
+ if (n < 0) {
+ return null;
+ }
+ T[] outVal = (T[]) ((clazz == null) ? new Object[n] : Array.newInstance(clazz, n));
+
+ for (int i = 0; i < n; i++) {
+ T value = readValue(loader, clazz);
+ outVal[i] = value;
+ }
+ return outVal;
+ }
+
+ /**
+ * The method is replaced by {@link #readSparseArray(ClassLoader, Class)}, however
+ * we are keeping this unused method here to allow unsupported app usages.
+ */
private void readSparseArrayInternal(@NonNull SparseArray outVal, int N,
@Nullable ClassLoader loader) {
while (N > 0) {
int key = readInt();
Object value = readValue(loader);
- //Log.i(TAG, "Unmarshalling key=" + key + " value=" + value);
outVal.append(key, value);
N--;
}
}
+ /**
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
+ */
+ @Nullable
+ private <T> SparseArray<T> readSparseArrayInternal(@Nullable ClassLoader loader,
+ @Nullable Class<? extends T> clazz) {
+ int n = readInt();
+ if (n < 0) {
+ return null;
+ }
+ SparseArray<T> outVal = new SparseArray<>(n);
+
+ while (n > 0) {
+ int key = readInt();
+ T value = readValue(loader, clazz);
+ outVal.append(key, value);
+ n--;
+ }
+ return outVal;
+ }
+
private void readSparseBooleanArrayInternal(@NonNull SparseBooleanArray outVal, int N) {
while (N > 0) {
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 387fc39..aa1bbc5 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5366,5 +5366,13 @@
public static final String COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS =
"d2d_sharing_contacts";
+ /**
+ * TelephonyProvider column name for NR Advanced calling
+ * Determines if the user has enabled VoNR settings for this subscription.
+ *
+ * @hide
+ */
+ public static final String COLUMN_NR_ADVANCED_CALLING_ENABLED =
+ "nr_advanced_calling_enabled";
}
}
diff --git a/core/java/android/security/attestationverification/OWNERS b/core/java/android/security/attestationverification/OWNERS
new file mode 100644
index 0000000..80a1f44
--- /dev/null
+++ b/core/java/android/security/attestationverification/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 1111194
+
+dlm@google.com
+dkrahn@google.com
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 9819648..096595f 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -26,7 +26,10 @@
import android.app.AlarmManager;
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -58,6 +61,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
+import java.util.function.Consumer;
/**
* Extend this class to implement a custom dream (available to the user as a "Daydream").
@@ -170,6 +175,13 @@
"android.service.dreams.DreamService";
/**
+ * The name of the extra where the dream overlay component is stored.
+ * @hide
+ */
+ public static final String EXTRA_DREAM_OVERLAY_COMPONENT =
+ "android.service.dream.DreamService.dream_overlay_component";
+
+ /**
* Name under which a Dream publishes information about itself.
* This meta-data must reference an XML resource containing
* a <code><{@link android.R.styleable#Dream dream}></code>
@@ -191,6 +203,7 @@
private boolean mCanDoze;
private boolean mDozing;
private boolean mWindowless;
+ private boolean mOverlayServiceBound;
private int mDozeScreenState = Display.STATE_UNKNOWN;
private int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
@@ -199,8 +212,62 @@
private DreamServiceWrapper mDreamServiceWrapper;
private Runnable mDispatchAfterOnAttachedToWindow;
+ private OverlayConnection mOverlayConnection;
+
+ private static class OverlayConnection implements ServiceConnection {
+ // Overlay set during onBind.
+ private IDreamOverlay mOverlay;
+ // A Queue of pending requests to execute on the overlay.
+ private ArrayDeque<Consumer<IDreamOverlay>> mRequests;
+
+ OverlayConnection() {
+ mRequests = new ArrayDeque<>();
+ }
+
+ public void request(Consumer<IDreamOverlay> request) {
+ mRequests.push(request);
+ evaluate();
+ }
+
+ private void evaluate() {
+ if (mOverlay == null) {
+ return;
+ }
+
+ // Any new requests that arrive during this loop will be processed synchronously after
+ // the loop exits.
+ while (!mRequests.isEmpty()) {
+ final Consumer<IDreamOverlay> request = mRequests.pop();
+ request.accept(mOverlay);
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ // Store Overlay and execute pending requests.
+ mOverlay = IDreamOverlay.Stub.asInterface(service);
+ evaluate();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ // Clear Overlay binder to prevent further request processing.
+ mOverlay = null;
+ }
+ }
+
+ private IDreamOverlayCallback mOverlayCallback = new IDreamOverlayCallback.Stub() {
+ @Override
+ public void onExitRequested() {
+ // Simply finish dream when exit is requested.
+ finish();
+ }
+ };
+
+
public DreamService() {
mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE));
+ mOverlayConnection = new OverlayConnection();
}
/**
@@ -861,6 +928,18 @@
public final IBinder onBind(Intent intent) {
if (mDebug) Slog.v(TAG, "onBind() intent = " + intent);
mDreamServiceWrapper = new DreamServiceWrapper();
+
+ // Connect to the overlay service if present.
+ final ComponentName overlayComponent =
+ intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT);
+ if (overlayComponent != null && !mWindowless) {
+ final Intent overlayIntent = new Intent();
+ overlayIntent.setComponent(overlayComponent);
+
+ mOverlayServiceBound = getApplicationContext().bindService(overlayIntent,
+ mOverlayConnection, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE);
+ }
+
return mDreamServiceWrapper;
}
@@ -894,6 +973,11 @@
return;
}
+ if (!mWindowless && mOverlayServiceBound) {
+ unbindService(mOverlayConnection);
+ mOverlayServiceBound = false;
+ }
+
try {
// finishSelf will unbind the dream controller from the dream service. This will
// trigger DreamService.this.onDestroy and DreamService.this will die.
@@ -1101,6 +1185,16 @@
}
}
});
+
+ // Request the DreamOverlay be told to dream with dream's window parameters once the service
+ // has connected.
+ mOverlayConnection.request(overlay -> {
+ try {
+ overlay.startDream(mWindow.getAttributes(), mOverlayCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "could not send window attributes:" + e);
+ }
+ });
}
private boolean getWindowFlagValue(int flag, boolean defaultValue) {
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 0ce9cfa..3e0deeb 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -41,4 +41,5 @@
void forceAmbientDisplayEnabled(boolean enabled);
ComponentName[] getDreamComponentsForUser(int userId);
void setDreamComponentsForUser(int userId, in ComponentName[] componentNames);
+ void registerDreamOverlayService(in ComponentName componentName);
}
diff --git a/core/java/android/service/dreams/IDreamOverlay.aidl b/core/java/android/service/dreams/IDreamOverlay.aidl
new file mode 100644
index 0000000..2b6633d
--- /dev/null
+++ b/core/java/android/service/dreams/IDreamOverlay.aidl
@@ -0,0 +1,37 @@
+/**
+ * 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.service.dreams;
+
+import android.service.dreams.IDreamOverlayCallback;
+import android.view.WindowManager.LayoutParams;
+
+/**
+* {@link IDreamOverlay} provides a way for a component to annotate a dream with additional view
+* elements. Registered through the DreamManager, a IDreamOverlay is bound to by the dream and
+* passed the necessary window details to participate in the user interface.
+
+* @hide
+*/
+interface IDreamOverlay {
+ /**
+ * @param params The {@link LayoutParams} for the associated DreamWindow, including the window
+ token of the Dream Activity.
+ * @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the
+ * dream.
+ */
+ void startDream(in LayoutParams params, in IDreamOverlayCallback callback);
+}
diff --git a/core/java/android/service/dreams/IDreamOverlayCallback.aidl b/core/java/android/service/dreams/IDreamOverlayCallback.aidl
new file mode 100644
index 0000000..ec76a33
--- /dev/null
+++ b/core/java/android/service/dreams/IDreamOverlayCallback.aidl
@@ -0,0 +1,31 @@
+/**
+ * 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.service.dreams;
+
+/**
+* {@link IDreamOverlayCallback} defines the interactions a dream overlay can have with its
+* associated dream. It is the discretion of the {@link DreamService}) to honor any inbound requests
+* from this callback.
+*
+* @hide
+*/
+interface IDreamOverlayCallback {
+ /**
+ * Invoked to request the dream exit.
+ */
+ void onExitRequested();
+}
\ No newline at end of file
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index d12ed8f..1460cb2 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -832,13 +832,45 @@
* consuming content. May be consumed by system to set account globally.
*/
public static final int KEYCODE_PROFILE_SWITCH = 288;
+ /** Key code constant: Video Application key #1. */
+ public static final int KEYCODE_VIDEO_APP_1 = 289;
+ /** Key code constant: Video Application key #2. */
+ public static final int KEYCODE_VIDEO_APP_2 = 290;
+ /** Key code constant: Video Application key #3. */
+ public static final int KEYCODE_VIDEO_APP_3 = 291;
+ /** Key code constant: Video Application key #4. */
+ public static final int KEYCODE_VIDEO_APP_4 = 292;
+ /** Key code constant: Video Application key #5. */
+ public static final int KEYCODE_VIDEO_APP_5 = 293;
+ /** Key code constant: Video Application key #6. */
+ public static final int KEYCODE_VIDEO_APP_6 = 294;
+ /** Key code constant: Video Application key #7. */
+ public static final int KEYCODE_VIDEO_APP_7 = 295;
+ /** Key code constant: Video Application key #8. */
+ public static final int KEYCODE_VIDEO_APP_8 = 296;
+ /** Key code constant: Featured Application key #1. */
+ public static final int KEYCODE_FEATURED_APP_1 = 297;
+ /** Key code constant: Featured Application key #2. */
+ public static final int KEYCODE_FEATURED_APP_2 = 298;
+ /** Key code constant: Featured Application key #3. */
+ public static final int KEYCODE_FEATURED_APP_3 = 299;
+ /** Key code constant: Featured Application key #4. */
+ public static final int KEYCODE_FEATURED_APP_4 = 300;
+ /** Key code constant: Demo Application key #1. */
+ public static final int KEYCODE_DEMO_APP_1 = 301;
+ /** Key code constant: Demo Application key #2. */
+ public static final int KEYCODE_DEMO_APP_2 = 302;
+ /** Key code constant: Demo Application key #3. */
+ public static final int KEYCODE_DEMO_APP_3 = 303;
+ /** Key code constant: Demo Application key #4. */
+ public static final int KEYCODE_DEMO_APP_4 = 304;
- /**
+ /**
* Integer value of the last KEYCODE. Increases as new keycodes are added to KeyEvent.
* @hide
*/
@TestApi
- public static final int LAST_KEYCODE = KEYCODE_PROFILE_SWITCH;
+ public static final int LAST_KEYCODE = KEYCODE_DEMO_APP_4;
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index e1c4305..2b79bbf 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -19,7 +19,7 @@
import android.annotation.IntDef;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
-import android.os.SystemProperties;
+import android.sysprop.InputProperties;
import android.util.ArrayMap;
import android.util.Pools.SynchronizedPool;
@@ -279,8 +279,7 @@
// If user has not selected a specific strategy
if (strategy == VELOCITY_TRACKER_STRATEGY_DEFAULT) {
// Check if user specified strategy by overriding system property.
- String strategyProperty =
- SystemProperties.get("persist.input.velocitytracker.strategy");
+ String strategyProperty = InputProperties.velocitytracker_strategy().orElse(null);
if (strategyProperty == null || strategyProperty.isEmpty()) {
mStrategy = strategy;
} else {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 89e1e08..4879206 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -57,12 +57,15 @@
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+import static android.view.WindowLayout.UNSPECIFIED_LENGTH;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED;
@@ -72,6 +75,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
@@ -90,6 +94,7 @@
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ResourcesManager;
+import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -515,6 +520,8 @@
private final WindowLayout mWindowLayout = new WindowLayout();
+ private ViewRootImpl mParentViewRoot;
+
// This is used to reduce the race between window focus changes being dispatched from
// the window manager and input events coming through the input system.
@GuardedBy("this")
@@ -796,11 +803,7 @@
context);
mCompatibleVisibilityInfo = new SystemUiVisibilityInfo();
mAccessibilityManager = AccessibilityManager.getInstance(context);
- mAccessibilityManager.addAccessibilityStateChangeListener(
- mAccessibilityInteractionConnectionManager, mHandler);
mHighContrastTextManager = new HighContrastTextManager();
- mAccessibilityManager.addHighTextContrastStateChangeListener(
- mHighContrastTextManager, mHandler);
mViewConfiguration = ViewConfiguration.get(context);
mDensity = context.getResources().getDisplayMetrics().densityDpi;
mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
@@ -977,8 +980,6 @@
mView = view;
mAttachInfo.mDisplayState = mDisplay.getState();
- mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
-
mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
mFallbackEventHandler.setView(view);
mWindowAttributes.copyFrom(attrs);
@@ -1059,6 +1060,7 @@
if (panelParentView != null) {
mAttachInfo.mPanelParentWindowToken
= panelParentView.getApplicationWindowToken();
+ mParentViewRoot = panelParentView.getViewRootImpl();
}
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
@@ -1119,10 +1121,13 @@
final InsetsState state = mInsetsController.getState();
final Rect displayCutoutSafe = mTempRect;
state.getDisplayCutoutSafe(displayCutoutSafe);
+ final WindowConfiguration winConfig = getConfiguration().windowConfiguration;
mWindowLayout.computeWindowFrames(mWindowAttributes, state,
- displayCutoutSafe, getConfiguration().windowConfiguration.getBounds(),
+ displayCutoutSafe, winConfig.getBounds(), winConfig.getWindowingMode(),
+ UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH,
mInsetsController.getRequestedVisibilities(),
- null /* attachedWindowFrame */, mTmpFrames.frame, mTempRect2);
+ getAttachedWindowFrame(), 1f /* compactScale */,
+ mTmpFrames.displayFrame, mTempRect2, mTmpFrames.frame);
setFrame(mTmpFrames.frame);
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
@@ -1176,6 +1181,7 @@
"Unable to add window -- unknown error code " + res);
}
+ registerListeners();
if ((res & WindowManagerGlobal.ADD_FLAG_USE_BLAST) != 0) {
mUseBLASTAdapter = true;
}
@@ -1232,6 +1238,36 @@
}
}
+ private Rect getAttachedWindowFrame() {
+ final int type = mWindowAttributes.type;
+ final boolean layoutAttached = (mParentViewRoot != null
+ && type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW
+ && type != TYPE_APPLICATION_ATTACHED_DIALOG);
+ return layoutAttached ? mParentViewRoot.mWinFrame : null;
+ }
+
+ /**
+ * Register any kind of listeners if setView was success.
+ */
+ private void registerListeners() {
+ mAccessibilityManager.addAccessibilityStateChangeListener(
+ mAccessibilityInteractionConnectionManager, mHandler);
+ mAccessibilityManager.addHighTextContrastStateChangeListener(
+ mHighContrastTextManager, mHandler);
+ mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+ }
+
+ /**
+ * Unregister all listeners while detachedFromWindow.
+ */
+ private void unregisterListeners() {
+ mAccessibilityManager.removeAccessibilityStateChangeListener(
+ mAccessibilityInteractionConnectionManager);
+ mAccessibilityManager.removeHighTextContrastStateChangeListener(
+ mHighContrastTextManager);
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ }
+
private void setTag() {
final String[] split = mWindowAttributes.getTitle().toString().split("\\.");
if (split.length > 0) {
@@ -4912,10 +4948,6 @@
}
mAccessibilityInteractionConnectionManager.ensureNoConnection();
- mAccessibilityManager.removeAccessibilityStateChangeListener(
- mAccessibilityInteractionConnectionManager);
- mAccessibilityManager.removeHighTextContrastStateChangeListener(
- mHighContrastTextManager);
removeSendWindowContentChangedCallback();
destroyHardwareRenderer();
@@ -4948,8 +4980,7 @@
mInputEventReceiver = null;
}
- mDisplayManager.unregisterDisplayListener(mDisplayListener);
-
+ unregisterListeners();
unscheduleTraversals();
}
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
index cdc1977..7dfc95e 100644
--- a/core/java/android/view/WindowLayout.java
+++ b/core/java/android/view/WindowLayout.java
@@ -19,29 +19,44 @@
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.util.Log;
/**
* Computes window frames.
* @hide
*/
public class WindowLayout {
+ private static final String TAG = WindowLayout.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ public static final int UNSPECIFIED_LENGTH = -1;
+
private final Rect mTempDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
private final Rect mTempRect = new Rect();
public boolean computeWindowFrames(WindowManager.LayoutParams attrs, InsetsState state,
- Rect displayCutoutSafe, Rect windowBounds, InsetsVisibilities requestedVisibilities,
- Rect attachedWindowFrame, Rect outDisplayFrame, Rect outParentFrame) {
+ Rect displayCutoutSafe, Rect windowBounds, @WindowingMode int windowingMode,
+ int requestedWidth, int requestedHeight, InsetsVisibilities requestedVisibilities,
+ Rect attachedWindowFrame, float compatScale, Rect outDisplayFrame, Rect outParentFrame,
+ Rect outFrame) {
final int type = attrs.type;
final int fl = attrs.flags;
final int pfl = attrs.privateFlags;
@@ -72,18 +87,14 @@
}
// Compute bounds restricted by display cutout
+ final int cutoutMode = attrs.layoutInDisplayCutoutMode;
final DisplayCutout cutout = state.getDisplayCutout();
- if (cutout.isEmpty()) {
- return false;
- }
- boolean clippedByDisplayCutout = false;
final Rect displayCutoutSafeExceptMaybeBars = mTempDisplayCutoutSafeExceptMaybeBarsRect;
displayCutoutSafeExceptMaybeBars.set(displayCutoutSafe);
-
- // Ensure that windows with a non-ALWAYS display cutout mode are laid out in
- // the cutout safe zone.
- final int cutoutMode = attrs.layoutInDisplayCutoutMode;
- if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
+ boolean clippedByDisplayCutout = false;
+ if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS && !cutout.isEmpty()) {
+ // Ensure that windows with a non-ALWAYS display cutout mode are laid out in
+ // the cutout safe zone.
final Rect displayFrame = state.getDisplayFrame();
final InsetsSource statusBarSource = state.peekSource(ITYPE_STATUS_BAR);
if (statusBarSource != null && displayCutoutSafe.top > displayFrame.top) {
@@ -147,6 +158,118 @@
}
outDisplayFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
}
+
+ final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
+ final boolean inMultiWindowMode = WindowConfiguration.inMultiWindowMode(windowingMode);
+
+ // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
+ // Also, we don't allow windows in multi-window mode to extend out of the screen.
+ if (noLimits && type != TYPE_SYSTEM_ERROR && !inMultiWindowMode) {
+ outDisplayFrame.left = outDisplayFrame.top = -10000;
+ outDisplayFrame.right = outDisplayFrame.bottom = 10000;
+ }
+
+ final boolean hasCompatScale = compatScale != 1f;
+ final int pw = outParentFrame.width();
+ final int ph = outParentFrame.height();
+ int rw = requestedWidth;
+ int rh = requestedHeight;
+ float x, y;
+ int w, h;
+
+ // If the view hierarchy hasn't been measured, the requested width and height would be
+ // UNSPECIFIED_LENGTH. This can happen in the first layout of a window or in the simulated
+ // layout.
+ if (rw == UNSPECIFIED_LENGTH) {
+ rw = attrs.width >= 0 ? attrs.width : pw;
+ }
+ if (rh == UNSPECIFIED_LENGTH) {
+ rh = attrs.height >= 0 ? attrs.height : ph;
+ }
+
+ if ((attrs.flags & FLAG_SCALED) != 0) {
+ if (attrs.width < 0) {
+ w = pw;
+ } else if (hasCompatScale) {
+ w = (int) (attrs.width * compatScale + .5f);
+ } else {
+ w = attrs.width;
+ }
+ if (attrs.height < 0) {
+ h = ph;
+ } else if (hasCompatScale) {
+ h = (int) (attrs.height * compatScale + .5f);
+ } else {
+ h = attrs.height;
+ }
+ } else {
+ if (attrs.width == MATCH_PARENT) {
+ w = pw;
+ } else if (hasCompatScale) {
+ w = (int) (rw * compatScale + .5f);
+ } else {
+ w = rw;
+ }
+ if (attrs.height == MATCH_PARENT) {
+ h = ph;
+ } else if (hasCompatScale) {
+ h = (int) (rh * compatScale + .5f);
+ } else {
+ h = rh;
+ }
+ }
+
+ if (hasCompatScale) {
+ x = attrs.x * compatScale;
+ y = attrs.y * compatScale;
+ } else {
+ x = attrs.x;
+ y = attrs.y;
+ }
+
+ if (inMultiWindowMode
+ && (attrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) == 0) {
+ // Make sure window fits in parent frame since it is in a non-fullscreen task as
+ // required by {@link Gravity#apply} call.
+ w = Math.min(w, pw);
+ h = Math.min(h, ph);
+ }
+
+ // We need to fit it to the display if either
+ // a) The window is in a fullscreen container, or we don't have a task (we assume fullscreen
+ // for the taskless windows)
+ // b) If it's a secondary app window, we also need to fit it to the display unless
+ // FLAG_LAYOUT_NO_LIMITS is set. This is so we place Popups, dialogs, and similar windows on
+ // screen, but SurfaceViews want to be always at a specific location so we don't fit it to
+ // the display.
+ final boolean fitToDisplay = !inMultiWindowMode
+ || ((attrs.type != TYPE_BASE_APPLICATION) && !noLimits);
+
+ // Set mFrame
+ Gravity.apply(attrs.gravity, w, h, outParentFrame,
+ (int) (x + attrs.horizontalMargin * pw),
+ (int) (y + attrs.verticalMargin * ph), outFrame);
+ // Now make sure the window fits in the overall display frame.
+ if (fitToDisplay) {
+ Gravity.applyDisplay(attrs.gravity, outDisplayFrame, outFrame);
+ }
+
+ if (DEBUG) Log.d(TAG, "computeWindowFrames " + attrs.getTitle()
+ + " outFrame=" + outFrame.toShortString()
+ + " outParentFrame=" + outParentFrame.toShortString()
+ + " outDisplayFrame=" + outDisplayFrame.toShortString()
+ + " attachedWindowFrame=" + (attachedWindowFrame != null
+ ? attachedWindowFrame.toShortString()
+ : "null")
+ + " requestedWidth=" + requestedWidth
+ + " requestedHeight=" + requestedHeight
+ + " compatScale=" + compatScale
+ + " windowingMode=" + WindowConfiguration.windowingModeToString(windowingMode)
+ + " displayCutoutSafe=" + displayCutoutSafe
+ + " attrs=" + attrs
+ + " state=" + state
+ + " requestedVisibilities=" + requestedVisibilities);
+
return clippedByDisplayCutout;
}
}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 3db7136..69b4187 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -371,6 +371,10 @@
Log.v(TAG, "onVirtualViewTranslationCompleted: received response for "
+ "AutofillId " + autofillId);
}
+ view.onVirtualViewTranslationResponses(virtualChildResponse);
+ if (mCurrentState == STATE_UI_TRANSLATION_PAUSED) {
+ return;
+ }
mActivity.runOnUiThread(() -> {
if (view.getViewTranslationCallback() == null) {
if (DEBUG) {
@@ -379,7 +383,6 @@
}
return;
}
- view.onVirtualViewTranslationResponses(virtualChildResponse);
if (view.getViewTranslationCallback() != null) {
view.getViewTranslationCallback().onShowTranslation(view);
}
@@ -427,6 +430,8 @@
+ " may be gone.");
continue;
}
+ int currentState;
+ currentState = mCurrentState;
mActivity.runOnUiThread(() -> {
ViewTranslationCallback callback = view.getViewTranslationCallback();
if (view.getViewTranslationResponse() != null
@@ -460,6 +465,9 @@
callback.enableContentPadding();
}
view.onViewTranslationResponse(response);
+ if (currentState == STATE_UI_TRANSLATION_PAUSED) {
+ return;
+ }
callback.onShowTranslation(view);
});
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index fe5eb08..0f309f1 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -34,6 +34,7 @@
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.Application;
+import android.app.LoadedApk;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.appwidget.AppWidgetHostView;
@@ -5475,7 +5476,8 @@
// user. So build a context that loads resources from that user but
// still returns the current users userId so settings like data / time formats
// are loaded without requiring cross user persmissions.
- final Context contextForResources = getContextForResources(context);
+ final Context contextForResources =
+ getContextForResourcesEnsuringCorrectCachedApkPaths(context);
if (colorResources != null) {
colorResources.apply(contextForResources);
}
@@ -5853,13 +5855,14 @@
}
}
- private Context getContextForResources(Context context) {
+ private Context getContextForResourcesEnsuringCorrectCachedApkPaths(Context context) {
if (mApplication != null) {
if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
&& context.getPackageName().equals(mApplication.packageName)) {
return context;
}
try {
+ LoadedApk.checkAndUpdateApkPaths(mApplication);
return context.createApplicationContext(mApplication,
Context.CONTEXT_RESTRICTED);
} catch (NameNotFoundException e) {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 6b33428..8e293f4 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -408,7 +408,7 @@
}
@Override
- protected Context getRemoteContext() {
+ protected Context getRemoteContextEnsuringCorrectCachedApkPath() {
return null;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3eaf852..8fba583 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8751,6 +8751,11 @@
return mEditor != null && mEditor.mInputType != EditorInfo.TYPE_NULL;
}
+ private boolean hasEditorInFocusSearchDirection(@FocusRealDirection int direction) {
+ final View nextView = focusSearch(direction);
+ return nextView != null && nextView.onCheckIsTextEditor();
+ }
+
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
if (onCheckIsTextEditor() && isEnabled()) {
@@ -8767,10 +8772,10 @@
outAttrs.imeOptions = EditorInfo.IME_NULL;
outAttrs.hintLocales = null;
}
- if (focusSearch(FOCUS_DOWN) != null) {
+ if (hasEditorInFocusSearchDirection(FOCUS_DOWN)) {
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
}
- if (focusSearch(FOCUS_UP) != null) {
+ if (hasEditorInFocusSearchDirection(FOCUS_UP)) {
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS;
}
if ((outAttrs.imeOptions & EditorInfo.IME_MASK_ACTION)
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index d259b7f..87e8ac1 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -473,6 +473,7 @@
}
}
mCurCombinedState = state;
+ mStats.mUidStates.get(mUid).updateCombinedState(state, now);
}
}
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index fd16662..b7abe73 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -179,17 +179,19 @@
public static final int REPORT_PKG_ASC_STATS = 0x08;
// Should report package stats.
public static final int REPORT_PKG_STATS = 0x0E;
+ // Should report uid stats.
+ public static final int REPORT_UID_STATS = 0x10;
// Should report all stats.
- public static final int REPORT_ALL = 0x0F;
+ public static final int REPORT_ALL = 0x1F;
public static final int[] OPTIONS =
{REPORT_PROC_STATS, REPORT_PKG_PROC_STATS, REPORT_PKG_SVC_STATS, REPORT_PKG_ASC_STATS,
- REPORT_PKG_STATS, REPORT_ALL};
+ REPORT_PKG_STATS, REPORT_UID_STATS, REPORT_ALL};
public static final String[] OPTIONS_STR =
- {"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"};
+ {"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "uid", "all"};
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 40;
+ private static final int PARCEL_VERSION = 41;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535454;
@@ -200,6 +202,8 @@
public final ProcessMap<LongSparseArray<PackageState>> mPackages = new ProcessMap<>();
public final ProcessMap<ProcessState> mProcesses = new ProcessMap<>();
+ public final SparseArray<UidState> mUidStates = new SparseArray<>();
+
public final ArrayList<AssociationState.SourceState> mTrackingAssociations = new ArrayList<>();
public final long[] mMemFactorDurations = new long[ADJ_COUNT];
@@ -337,6 +341,18 @@
}
}
+ SparseArray<UidState> uidStates = other.mUidStates;
+ for (int ip = 0, size = uidStates.size(); ip < size; ip++) {
+ final int uid = uidStates.keyAt(ip);
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null) {
+ uidState = uidStates.valueAt(ip).clone();
+ mUidStates.put(uid, uidState);
+ } else {
+ uidState.add(uidStates.valueAt(ip));
+ }
+ }
+
ArrayMap<String, SparseArray<ProcessState>> procMap = other.mProcesses.getMap();
for (int ip=0; ip<procMap.size(); ip++) {
SparseArray<ProcessState> uids = procMap.valueAt(ip);
@@ -358,9 +374,21 @@
}
}
thisProc.add(otherProc);
+ if (thisProc.isActive()) {
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null) {
+ uidState = new UidState(this, uid);
+ mUidStates.put(uid, uidState);
+ }
+ uidState.addProcess(thisProc);
+ }
}
}
+ for (int ip = 0, size = mUidStates.size(); ip < size; ip++) {
+ mUidStates.valueAt(ip).updateCombinedState(-1);
+ }
+
for (int i=0; i<ADJ_COUNT; i++) {
if (DEBUG) Slog.d(TAG, "Total duration #" + i + " inc by "
+ other.mMemFactorDurations[i] + " from "
@@ -488,6 +516,7 @@
resetCommon();
mPackages.getMap().clear();
mProcesses.getMap().clear();
+ mUidStates.clear();
mMemFactor = STATE_NOTHING;
mStartTime = 0;
if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr);
@@ -580,6 +609,7 @@
}
} else {
ps.makeDead();
+ mUidStates.get(uids.keyAt(iu)).removeProcess(ps, now);
uids.removeAt(iu);
}
}
@@ -588,6 +618,15 @@
}
}
+ for (int ip = mUidStates.size() - 1; ip >= 0; ip--) {
+ final UidState uidState = mUidStates.valueAt(ip);
+ if (uidState.isInUse()) {
+ mUidStates.valueAt(ip).resetSafely(now);
+ } else {
+ mUidStates.removeAt(ip);
+ }
+ }
+
mStartTime = now;
if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr);
}
@@ -900,6 +939,13 @@
mSysMemUsage.writeToParcel(out);
+ final int numOfUids = mUidStates.size();
+ out.writeInt(numOfUids);
+ for (int ip = 0; ip < numOfUids; ip++) {
+ out.writeInt(mUidStates.keyAt(ip));
+ mUidStates.valueAt(ip).writeToParcel(out, now);
+ }
+
out.writeInt(NPROC);
for (int ip=0; ip<NPROC; ip++) {
writeCommonString(out, procMap.keyAt(ip));
@@ -1026,7 +1072,8 @@
public void readFromParcel(Parcel in) {
final boolean hadData = mPackages.getMap().size() > 0
- || mProcesses.getMap().size() > 0;
+ || mProcesses.getMap().size() > 0
+ || mUidStates.size() > 0;
if (hadData) {
resetSafely();
}
@@ -1083,6 +1130,16 @@
return;
}
+ final int numOfUids = in.readInt();
+ for (int ip = 0; ip < numOfUids; ip++) {
+ final int uid = in.readInt();
+ final UidState uidState = new UidState(this, uid);
+ if (!uidState.readFromParcel(in)) {
+ return;
+ }
+ mUidStates.put(uid, uidState);
+ }
+
int NPROC = in.readInt();
if (NPROC < 0) {
mReadError = "bad process count: " + NPROC;
@@ -1127,9 +1184,17 @@
if (DEBUG_PARCEL) Slog.d(TAG, "Adding process: " + procName + " " + uid
+ " " + proc);
mProcesses.put(procName, uid, proc);
+
+ if (proc.isActive()) {
+ mUidStates.get(uid).addProcess(proc);
+ }
}
}
+ for (int ip = 0; ip < numOfUids; ip++) {
+ mUidStates.valueAt(ip).updateCombinedState(-1);
+ }
+
if (DEBUG_PARCEL) Slog.d(TAG, "Read " + mProcesses.getMap().size() + " processes");
int NPKG = in.readInt();
@@ -1327,6 +1392,12 @@
commonProc = new ProcessState(this, pkgState.mPackageName, pkgState.mUid,
pkgState.mVersionCode, processName);
mProcesses.put(processName, pkgState.mUid, commonProc);
+ UidState uidState = mUidStates.get(pkgState.mUid);
+ if (uidState == null) {
+ uidState = new UidState(this, pkgState.mUid);
+ mUidStates.put(pkgState.mUid, uidState);
+ }
+ uidState.addProcess(commonProc);
if (DEBUG) Slog.d(TAG, "GETPROC created new common " + commonProc);
}
if (!commonProc.isMultiPackage()) {
@@ -1787,6 +1858,42 @@
pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
}
+ if ((section & REPORT_UID_STATS) != 0) {
+ SparseArray<UidState> uidStates = mUidStates;
+ int numShownUids = 0, numTotalUids = 0;
+ printedHeader = false;
+ for (int iu = 0, size = uidStates.size(); iu < size; iu++) {
+ final int uid = uidStates.keyAt(iu);
+ final UidState uidState = uidStates.valueAt(iu);
+ numTotalUids++;
+ if (reqPackage != null && !uidState.hasPackage(reqPackage)) {
+ continue;
+ }
+ numShownUids++;
+ pw.println();
+ if (!printedHeader) {
+ pw.println("Per-UID Stats:");
+ printedHeader = true;
+ }
+ if (activeOnly && !uidState.isInUse()) {
+ pw.print(" (Not active: ");
+ pw.print(UserHandle.formatUid(uid));
+ pw.println(")");
+ continue;
+ }
+ pw.print(" * ");
+ UserHandle.formatUid(pw, uid);
+ pw.print(" (");
+ pw.print(uidState.getDurationsBucketCount());
+ pw.print(" entries)");
+ pw.println(":");
+ uidState.dumpState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES, now);
+ }
+ pw.print(" Total UIDs: "); pw.print(numShownUids);
+ pw.print(" shown of "); pw.print(numTotalUids); pw.println(" total");
+ }
+
if (dumpAll) {
pw.println();
if (mTrackingAssociations.size() > 0) {
diff --git a/core/java/com/android/internal/app/procstats/ProcessStatsInternal.java b/core/java/com/android/internal/app/procstats/ProcessStatsInternal.java
new file mode 100644
index 0000000..2f23008
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/ProcessStatsInternal.java
@@ -0,0 +1,31 @@
+/*
+ * 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.internal.app.procstats;
+
+import android.util.SparseArray;
+
+/**
+ * The internal interface to access the process stats service, to be used within
+ * system_server only.
+ */
+public abstract class ProcessStatsInternal {
+ /**
+ * Return the duration over the given time, that an UID spent in each processs state
+ * which is defined in the {@link ProcessStats}, the key of the array is the uid.
+ */
+ public abstract SparseArray<long[]> getUidProcStateStatsOverTime(long minTime);
+}
diff --git a/core/java/com/android/internal/app/procstats/UidState.java b/core/java/com/android/internal/app/procstats/UidState.java
new file mode 100644
index 0000000..8761b74
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/UidState.java
@@ -0,0 +1,315 @@
+/*
+ * 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.internal.app.procstats;
+
+import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
+
+import android.os.Parcel;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.TimeUtils;
+
+import java.io.PrintWriter;
+
+/**
+ * The class to track the individual time-in-state of a UID.
+ */
+public final class UidState {
+ private static final String TAG = "ProcessStats";
+
+ private final ProcessStats mStats;
+ private final int mUid;
+ private final DurationsTable mDurations;
+
+ private ArraySet<ProcessState> mProcesses = new ArraySet<>();
+ private int mCurCombinedState = STATE_NOTHING;
+ private long mStartTime;
+
+ private long mTotalRunningStartTime;
+ private long mTotalRunningDuration;
+
+ /**
+ * Create a new UID state. The initial state is not running.
+ */
+ public UidState(ProcessStats processStats, int uid) {
+ mStats = processStats;
+ mUid = uid;
+ mDurations = new DurationsTable(processStats.mTableData);
+ }
+
+ /**
+ * Create a copy of this instance.
+ */
+ public UidState clone() {
+ UidState unew = new UidState(mStats, mUid);
+ unew.mDurations.addDurations(mDurations);
+ unew.mCurCombinedState = mCurCombinedState;
+ unew.mStartTime = mStartTime;
+ unew.mTotalRunningStartTime = mTotalRunningStartTime;
+ unew.mTotalRunningDuration = mTotalRunningDuration;
+ return unew;
+ }
+
+ /**
+ * Update the current state of the UID, it should be a combination
+ * of all running processes in this UID.
+ */
+ public void updateCombinedState(int state, long now) {
+ if (mCurCombinedState != state) {
+ updateCombinedState(now);
+ }
+ }
+
+ /**
+ * Update the current state of the UID, it should be a combination
+ * of all running processes in this UID.
+ */
+ public void updateCombinedState(long now) {
+ setCombinedStateInner(calcCombinedState(), now);
+ }
+
+ private int calcCombinedState() {
+ int minCombined = STATE_NOTHING;
+ int min = STATE_NOTHING;
+ for (int i = 0, size = mProcesses.size(); i < size; i++) {
+ final int combinedState = mProcesses.valueAt(i).getCombinedState();
+ final int state = combinedState % STATE_COUNT;
+ if (combinedState != STATE_NOTHING) {
+ if (min == STATE_NOTHING || state < min) {
+ minCombined = combinedState;
+ min = state;
+ }
+ }
+ }
+ return minCombined;
+ }
+
+ /**
+ * Set the combined state and commit the state.
+ *
+ * @param now When it's negative, the previous state won't be committed.
+ */
+ private void setCombinedStateInner(int state, long now) {
+ if (mCurCombinedState != state) {
+ if (now >= 0) {
+ commitStateTime(now);
+ if (state == STATE_NOTHING) {
+ // We are transitioning to a no longer running state... stop counting run time.
+ mTotalRunningDuration += now - mTotalRunningStartTime;
+ } else if (mCurCombinedState == STATE_NOTHING) {
+ // We previously weren't running... now starting again, clear out total
+ // running info.
+ mTotalRunningDuration = 0;
+ }
+ }
+ mCurCombinedState = state;
+ }
+ }
+
+ /**
+ * @return The current combine state of the UID.
+ */
+ public int getCombinedState() {
+ return mCurCombinedState;
+ }
+
+ /**
+ * Commit the current state's duration into stats.
+ */
+ public void commitStateTime(long now) {
+ if (mCurCombinedState != STATE_NOTHING) {
+ long dur = now - mStartTime;
+ if (dur > 0) {
+ mDurations.addDuration(mCurCombinedState, dur);
+ }
+ mTotalRunningDuration += now - mTotalRunningStartTime;
+ mTotalRunningStartTime = now;
+ }
+ mStartTime = now;
+ }
+
+ /**
+ * Reset the UID stats safely.
+ */
+ public void resetSafely(long now) {
+ mDurations.resetTable();
+ mStartTime = now;
+ }
+
+ /**
+ * @return Whether this UID stats is still being used or not.
+ */
+ public boolean isInUse() {
+ for (int i = 0, size = mProcesses.size(); i < size; i++) {
+ if (mProcesses.valueAt(i).isInUse()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return Whether the given package belongs to this UID or not.
+ */
+ public boolean hasPackage(String packageName) {
+ for (int i = 0, size = mProcesses.size(); i < size; i++) {
+ final ProcessState proc = mProcesses.valueAt(i);
+ if (TextUtils.equals(packageName, proc.getName())
+ && TextUtils.equals(packageName, proc.getPackage())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add stats data from another instance to this one.
+ */
+ public void add(UidState other) {
+ mDurations.addDurations(other.mDurations);
+ mTotalRunningDuration += other.mTotalRunningDuration;
+ }
+
+ void addProcess(ProcessState proc) {
+ mProcesses.add(proc);
+ }
+
+ void addProcess(ProcessState proc, long now) {
+ mProcesses.add(proc);
+ setCombinedStateInner(proc.getCombinedState(), now);
+ }
+
+ void removeProcess(ProcessState proc, long now) {
+ mProcesses.remove(proc);
+ setCombinedStateInner(proc.getCombinedState(), now);
+ }
+
+ /**
+ * @return The total amount of stats it's currently tracking.
+ */
+ public int getDurationsBucketCount() {
+ return mDurations.getKeyCount();
+ }
+
+ /**
+ * @return The total running duration of this UID.
+ */
+ public long getTotalRunningDuration(long now) {
+ return mTotalRunningDuration
+ + (mTotalRunningStartTime != 0 ? (now - mTotalRunningStartTime) : 0);
+ }
+
+ /**
+ * @return The duration in the given state.
+ */
+ public long getDuration(int state, long now) {
+ long time = mDurations.getValueForId((byte) state);
+ if (mCurCombinedState == state) {
+ time += now - mStartTime;
+ }
+ return time;
+ }
+
+ /**
+ * @return The durations in each process state, the mem/screen factors
+ * are consolidated into the bucket with the same process state.
+ */
+ public long[] getAggregatedDurationsInStates() {
+ final long[] states = new long[STATE_COUNT];
+ final int numOfBuckets = getDurationsBucketCount();
+ for (int i = 0; i < numOfBuckets; i++) {
+ final int key = mDurations.getKeyAt(i);
+ final int combinedState = SparseMappingTable.getIdFromKey(key);
+ states[combinedState % STATE_COUNT] += mDurations.getValue(key);
+ }
+ return states;
+ }
+
+ void writeToParcel(Parcel out, long now) {
+ mDurations.writeToParcel(out);
+ out.writeLong(getTotalRunningDuration(now));
+ }
+
+ boolean readFromParcel(Parcel in) {
+ if (!mDurations.readFromParcel(in)) {
+ return false;
+ }
+ mTotalRunningDuration = in.readLong();
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("UidState{").append(Integer.toHexString(System.identityHashCode(this)))
+ .append(" ").append(UserHandle.formatUid(mUid)).append("}");
+ return sb.toString();
+ }
+
+ void dumpState(PrintWriter pw, String prefix,
+ int[] screenStates, int[] memStates, int[] procStates, long now) {
+ long totalTime = 0;
+ int printedScreen = -1;
+ for (int is = 0; is < screenStates.length; is++) {
+ int printedMem = -1;
+ for (int im = 0; im < memStates.length; im++) {
+ for (int ip = 0; ip < procStates.length; ip++) {
+ final int iscreen = screenStates[is];
+ final int imem = memStates[im];
+ final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
+ long time = mDurations.getValueForId((byte) bucket);
+ String running = "";
+ if (mCurCombinedState == bucket) {
+ running = " (running)";
+ time += now - mStartTime;
+ }
+ if (time != 0) {
+ pw.print(prefix);
+ if (screenStates.length > 1) {
+ DumpUtils.printScreenLabel(pw, printedScreen != iscreen
+ ? iscreen : STATE_NOTHING);
+ printedScreen = iscreen;
+ }
+ if (memStates.length > 1) {
+ DumpUtils.printMemLabel(pw,
+ printedMem != imem ? imem : STATE_NOTHING, '/');
+ printedMem = imem;
+ }
+ pw.print(DumpUtils.STATE_LABELS[procStates[ip]]); pw.print(": ");
+ TimeUtils.formatDuration(time, pw); pw.println(running);
+ totalTime += time;
+ }
+ }
+ }
+ }
+ if (totalTime != 0) {
+ pw.print(prefix);
+ if (screenStates.length > 1) {
+ DumpUtils.printScreenLabel(pw, STATE_NOTHING);
+ }
+ if (memStates.length > 1) {
+ DumpUtils.printMemLabel(pw, STATE_NOTHING, '/');
+ }
+ pw.print(DumpUtils.STATE_LABEL_TOTAL);
+ pw.print(": ");
+ TimeUtils.formatDuration(totalTime, pw);
+ pw.println();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index 6c73e70..29b31d3 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -26,20 +26,25 @@
import android.util.IndentingPrintWriter;
import android.util.Log;
+import com.android.apex.ApexInfo;
+import com.android.apex.XmlParser;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.om.OverlayConfigParser.OverlayPartition;
import com.android.internal.content.om.OverlayConfigParser.ParsedConfiguration;
import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.TriConsumer;
import java.io.File;
+import java.io.FileInputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
-import java.util.function.BiConsumer;
import java.util.function.Supplier;
/**
@@ -77,7 +82,7 @@
public interface PackageProvider {
/** Performs the given action for each package. */
- void forEachPackage(BiConsumer<ParsingPackageRead, Boolean> p);
+ void forEachPackage(TriConsumer<ParsingPackageRead, Boolean, File> p);
}
private static final Comparator<ParsedConfiguration> sStaticOverlayComparator = (c1, c2) -> {
@@ -119,6 +124,8 @@
p)));
}
+ ArrayMap<Integer, List<String>> activeApexesPerPartition = getActiveApexes(partitions);
+
boolean foundConfigFile = false;
final Map<String, ParsedOverlayInfo> packageManagerOverlayInfos =
packageProvider == null ? null : getOverlayPackageInfos(packageProvider);
@@ -129,7 +136,9 @@
final OverlayScanner scanner = (scannerFactory == null) ? null : scannerFactory.get();
final ArrayList<ParsedConfiguration> partitionOverlays =
OverlayConfigParser.getConfigurations(partition, scanner,
- packageManagerOverlayInfos);
+ packageManagerOverlayInfos,
+ activeApexesPerPartition.getOrDefault(partition.type,
+ Collections.emptyList()));
if (partitionOverlays != null) {
foundConfigFile = true;
overlays.addAll(partitionOverlays);
@@ -147,7 +156,8 @@
// Filter out overlays not present in the partition.
partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos.values());
for (int j = partitionOverlayInfos.size() - 1; j >= 0; j--) {
- if (!partition.containsFile(partitionOverlayInfos.get(j).path)) {
+ if (!partition.containsFile(partitionOverlayInfos.get(j)
+ .getOriginalPartitionPath())) {
partitionOverlayInfos.remove(j);
}
}
@@ -294,16 +304,50 @@
private static Map<String, ParsedOverlayInfo> getOverlayPackageInfos(
@NonNull PackageProvider packageManager) {
final HashMap<String, ParsedOverlayInfo> overlays = new HashMap<>();
- packageManager.forEachPackage((ParsingPackageRead p, Boolean isSystem) -> {
+ packageManager.forEachPackage((ParsingPackageRead p, Boolean isSystem,
+ @Nullable File preInstalledApexPath) -> {
if (p.getOverlayTarget() != null && isSystem) {
overlays.put(p.getPackageName(), new ParsedOverlayInfo(p.getPackageName(),
p.getOverlayTarget(), p.getTargetSdkVersion(), p.isOverlayIsStatic(),
- p.getOverlayPriority(), new File(p.getBaseApkPath())));
+ p.getOverlayPriority(), new File(p.getBaseApkPath()),
+ preInstalledApexPath));
}
});
return overlays;
}
+ /** Returns a map of PartitionType to List of active APEX module names. */
+ @NonNull
+ private static ArrayMap<Integer, List<String>> getActiveApexes(
+ @NonNull List<OverlayPartition> partitions) {
+ // An Overlay in an APEX, which is an update of an APEX in a given partition,
+ // is considered as belonging to that partition.
+ ArrayMap<Integer, List<String>> result = new ArrayMap<>();
+ for (OverlayPartition partition : partitions) {
+ result.put(partition.type, new ArrayList<String>());
+ }
+ // Read from apex-info-list because ApexManager is not accessible to zygote.
+ File apexInfoList = new File("/apex/apex-info-list.xml");
+ if (apexInfoList.exists() && apexInfoList.canRead()) {
+ try (FileInputStream stream = new FileInputStream(apexInfoList)) {
+ List<ApexInfo> apexInfos = XmlParser.readApexInfoList(stream).getApexInfo();
+ for (ApexInfo info : apexInfos) {
+ if (info.getIsActive()) {
+ for (OverlayPartition partition : partitions) {
+ if (partition.containsPath(info.getPreinstalledModulePath())) {
+ result.get(partition.type).add(info.getModuleName());
+ break;
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Error reading apex-info-list: " + e);
+ }
+ }
+ return result;
+ }
+
/** Represents a single call to idmap create-multiple. */
@VisibleForTesting
public static class IdmapInvocation {
diff --git a/core/java/com/android/internal/content/om/OverlayConfigParser.java b/core/java/com/android/internal/content/om/OverlayConfigParser.java
index 053a341..0ab7b3d 100644
--- a/core/java/com/android/internal/content/om/OverlayConfigParser.java
+++ b/core/java/com/android/internal/content/om/OverlayConfigParser.java
@@ -41,6 +41,7 @@
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
/**
@@ -195,13 +196,19 @@
@Nullable
static ArrayList<ParsedConfiguration> getConfigurations(
@NonNull OverlayPartition partition, @Nullable OverlayScanner scanner,
- @Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos) {
- if (partition.getOverlayFolder() == null) {
- return null;
+ @Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos,
+ @NonNull List<String> activeApexes) {
+ if (scanner != null) {
+ if (partition.getOverlayFolder() != null) {
+ scanner.scanDir(partition.getOverlayFolder());
+ }
+ for (String apex : activeApexes) {
+ scanner.scanDir(new File("/apex/" + apex + "/overlay/"));
+ }
}
- if (scanner != null) {
- scanner.scanDir(partition.getOverlayFolder());
+ if (partition.getOverlayFolder() == null) {
+ return null;
}
final File configFile = new File(partition.getOverlayFolder(), CONFIG_DEFAULT_FILENAME);
diff --git a/core/java/com/android/internal/content/om/OverlayScanner.java b/core/java/com/android/internal/content/om/OverlayScanner.java
index 4387d1b..e4e0228 100644
--- a/core/java/com/android/internal/content/om/OverlayScanner.java
+++ b/core/java/com/android/internal/content/om/OverlayScanner.java
@@ -54,23 +54,38 @@
public final boolean isStatic;
public final int priority;
public final File path;
+ @Nullable public final File preInstalledApexPath;
public ParsedOverlayInfo(String packageName, String targetPackageName,
- int targetSdkVersion, boolean isStatic, int priority, File path) {
+ int targetSdkVersion, boolean isStatic, int priority, File path,
+ @Nullable File preInstalledApexPath) {
this.packageName = packageName;
this.targetPackageName = targetPackageName;
this.targetSdkVersion = targetSdkVersion;
this.isStatic = isStatic;
this.priority = priority;
this.path = path;
+ this.preInstalledApexPath = preInstalledApexPath;
}
@Override
public String toString() {
return getClass().getSimpleName() + String.format("{packageName=%s"
+ ", targetPackageName=%s, targetSdkVersion=%s, isStatic=%s"
- + ", priority=%s, path=%s}",
- packageName, targetPackageName, targetSdkVersion, isStatic, priority, path);
+ + ", priority=%s, path=%s, preInstalledApexPath=%s}",
+ packageName, targetPackageName, targetSdkVersion, isStatic,
+ priority, path, preInstalledApexPath);
+ }
+
+ /**
+ * Retrieves the path of the overlay in its original installation partition.
+ *
+ * An Overlay in an APEX, which is an update of an APEX in a given partition,
+ * is considered as belonging to that partition.
+ */
+ @NonNull
+ public File getOriginalPartitionPath() {
+ return preInstalledApexPath != null ? preInstalledApexPath : path;
}
}
@@ -189,6 +204,6 @@
}
return new ParsedOverlayInfo(apkLite.getPackageName(), apkLite.getTargetPackageName(),
apkLite.getTargetSdkVersion(), apkLite.isOverlayIsStatic(),
- apkLite.getOverlayPriority(), new File(apkLite.getPath()));
+ apkLite.getOverlayPriority(), new File(apkLite.getPath()), null);
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 985331c..1e20cfa 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -160,7 +160,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 202;
+ static final int VERSION = 203;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -237,6 +237,10 @@
return mKernelMemoryStats;
}
+ private static final int[] SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS = {
+ MeasuredEnergyStats.POWER_BUCKET_CPU,
+ };
+
@GuardedBy("this")
public boolean mPerProcStateCpuTimesAvailable = true;
@@ -526,6 +530,7 @@
* mPendingRemovedUids queue.
*/
@GuardedBy("this")
+ @SuppressWarnings("GuardedBy") // errorprone false positive on removeLocked
public void clearPendingRemovedUidsLocked() {
long cutOffTimeMs = mClock.elapsedRealtime() - mConstants.UID_REMOVE_DELAY_MS;
while (!mPendingRemovedUids.isEmpty()
@@ -1061,6 +1066,10 @@
int mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+ @GuardedBy("this")
+ @VisibleForTesting
+ protected @Nullable MeasuredEnergyStats.Config mMeasuredEnergyStatsConfig;
+
/**
* Accumulated global (generally, device-wide total) charge consumption of various consumers
* while on battery.
@@ -3564,6 +3573,7 @@
// from a battery level change.
static final int BATTERY_DELTA_LEVEL_FLAG = 0x00000001;
+ @GuardedBy("this")
public void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) {
if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) {
dest.writeInt(DELTA_TIME_ABS);
@@ -3836,11 +3846,13 @@
mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
}
+ @GuardedBy("this")
@Override
public void commitCurrentHistoryBatchLocked() {
mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
}
+ @GuardedBy("this")
public void createFakeHistoryEvents(long numEvents) {
final long elapsedRealtimeMs = mClock.elapsedRealtime();
final long uptimeMs = mClock.uptimeMillis();
@@ -3852,6 +3864,7 @@
}
}
+ @GuardedBy("this")
void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
@@ -3955,6 +3968,7 @@
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
}
+ @GuardedBy("this")
private void addHistoryBufferLocked(long elapsedRealtimeMs, byte cmd, HistoryItem cur) {
if (mBatteryStatsHistoryIterator != null) {
throw new IllegalStateException("Can't do this while iterating history!");
@@ -3981,6 +3995,7 @@
int mChangedStates = 0;
int mChangedStates2 = 0;
+ @GuardedBy("this")
void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mTrackRunningHistoryElapsedRealtimeMs != 0) {
final long diffElapsedMs = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtimeMs;
@@ -4001,10 +4016,12 @@
addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
}
+ @GuardedBy("this")
void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur);
}
+ @GuardedBy("this")
public void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
String name, int uid) {
mHistoryCur.eventCode = code;
@@ -4014,6 +4031,7 @@
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
+ @GuardedBy("this")
void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd, HistoryItem cur) {
HistoryItem rec = mHistoryCache;
if (rec != null) {
@@ -4026,6 +4044,7 @@
addHistoryRecordLocked(rec);
}
+ @GuardedBy("this")
void addHistoryRecordLocked(HistoryItem rec) {
mNumHistoryItems++;
rec.next = null;
@@ -4038,6 +4057,7 @@
}
}
+ @GuardedBy("this")
void clearHistoryLocked() {
if (DEBUG_HISTORY) Slog.i(TAG, "********** CLEARING HISTORY!");
mHistoryBaseTimeMs = 0;
@@ -4100,6 +4120,7 @@
}
}
+ @GuardedBy("this")
private void updateBatteryPropertiesLocked() {
try {
IBatteryPropertiesRegistrar registrar = IBatteryPropertiesRegistrar.Stub.asInterface(
@@ -4112,11 +4133,14 @@
}
}
+ @GuardedBy("this")
public void addIsolatedUidLocked(int isolatedUid, int appUid) {
addIsolatedUidLocked(isolatedUid, appUid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
+ @SuppressWarnings("GuardedBy") // errorprone false positive on u.addIsolatedUid
public void addIsolatedUidLocked(int isolatedUid, int appUid,
long elapsedRealtimeMs, long uptimeMs) {
mIsolatedUids.put(isolatedUid, appUid);
@@ -4191,10 +4215,12 @@
return isolated > 0 ? isolated : uid;
}
+ @GuardedBy("this")
public void noteEventLocked(int code, String name, int uid) {
noteEventLocked(code, name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteEventLocked(int code, String name, int uid,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
@@ -4204,6 +4230,7 @@
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, code, name, uid);
}
+ @GuardedBy("this")
public void noteCurrentTimeChangedLocked() {
final long currentTime = mClock.currentTimeMillis();
final long elapsedRealtime = mClock.elapsedRealtime();
@@ -4211,15 +4238,18 @@
noteCurrentTimeChangedLocked(currentTime, elapsedRealtime, uptime);
}
+ @GuardedBy("this")
public void noteCurrentTimeChangedLocked(long currentTimeMs,
long elapsedRealtimeMs, long uptimeMs) {
recordCurrentTimeChangeLocked(currentTimeMs, elapsedRealtimeMs, uptimeMs);
}
+ @GuardedBy("this")
public void noteProcessStartLocked(String name, int uid) {
noteProcessStartLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteProcessStartLocked(String name, int uid,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
@@ -4236,10 +4266,12 @@
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PROC_START, name, uid);
}
+ @GuardedBy("this")
public void noteProcessCrashLocked(String name, int uid) {
noteProcessCrashLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteProcessCrashLocked(String name, int uid,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
@@ -4249,10 +4281,12 @@
}
}
+ @GuardedBy("this")
public void noteProcessAnrLocked(String name, int uid) {
noteProcessAnrLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteProcessAnrLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (isOnBattery()) {
@@ -4261,10 +4295,13 @@
}
}
+ @GuardedBy("this")
public void noteUidProcessStateLocked(int uid, int state) {
noteUidProcessStateLocked(uid, state, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
+ @SuppressWarnings("GuardedBy") // errorprone false positive on u.updateUidProcessStateLocked
public void noteUidProcessStateLocked(int uid, int state,
long elapsedRealtimeMs, long uptimeMs) {
int parentUid = mapUid(uid);
@@ -4282,10 +4319,12 @@
.updateUidProcessStateLocked(state, elapsedRealtimeMs, uptimeMs);
}
+ @GuardedBy("this")
public void noteProcessFinishLocked(String name, int uid) {
noteProcessFinishLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteProcessFinishLocked(String name, int uid,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
@@ -4299,10 +4338,12 @@
name, uid);
}
+ @GuardedBy("this")
public void noteSyncStartLocked(String name, int uid) {
noteSyncStartLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteSyncStartLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -4313,10 +4354,12 @@
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SYNC_START, name, uid);
}
+ @GuardedBy("this")
public void noteSyncFinishLocked(String name, int uid) {
noteSyncFinishLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteSyncFinishLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -4328,10 +4371,12 @@
name, uid);
}
+ @GuardedBy("this")
public void noteJobStartLocked(String name, int uid) {
noteJobStartLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteJobStartLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -4342,11 +4387,13 @@
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_START, name, uid);
}
+ @GuardedBy("this")
public void noteJobFinishLocked(String name, int uid, int stopReason) {
noteJobFinishLocked(name, uid, stopReason,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteJobFinishLocked(String name, int uid, int stopReason,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
@@ -4358,11 +4405,13 @@
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_FINISH, name, uid);
}
+ @GuardedBy("this")
public void noteJobsDeferredLocked(int uid, int numDeferred, long sinceLast) {
noteJobsDeferredLocked(uid, numDeferred, sinceLast,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteJobsDeferredLocked(int uid, int numDeferred, long sinceLast,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
@@ -4370,34 +4419,33 @@
.noteJobsDeferredLocked(numDeferred, sinceLast);
}
+ @GuardedBy("this")
public void noteAlarmStartLocked(String name, WorkSource workSource, int uid) {
noteAlarmStartLocked(name, workSource, uid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteAlarmStartLocked(String name, WorkSource workSource, int uid,
long elapsedRealtimeMs, long uptimeMs) {
noteAlarmStartOrFinishLocked(HistoryItem.EVENT_ALARM_START, name, workSource, uid,
elapsedRealtimeMs, uptimeMs);
}
+ @GuardedBy("this")
public void noteAlarmFinishLocked(String name, WorkSource workSource, int uid) {
noteAlarmFinishLocked(name, workSource, uid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteAlarmFinishLocked(String name, WorkSource workSource, int uid,
long elapsedRealtimeMs, long uptimeMs) {
noteAlarmStartOrFinishLocked(HistoryItem.EVENT_ALARM_FINISH, name, workSource, uid,
elapsedRealtimeMs, uptimeMs);
}
- private void noteAlarmStartOrFinishLocked(int historyItem, String name, WorkSource workSource,
- int uid) {
- noteAlarmStartOrFinishLocked(historyItem, name, workSource, uid,
- mClock.elapsedRealtime(), mClock.uptimeMillis());
- }
-
+ @GuardedBy("this")
private void noteAlarmStartOrFinishLocked(int historyItem, String name, WorkSource workSource,
int uid, long elapsedRealtimeMs, long uptimeMs) {
if (!mRecordAllHistory) {
@@ -4430,12 +4478,14 @@
}
}
+ @GuardedBy("this")
public void noteWakupAlarmLocked(String packageName, int uid, WorkSource workSource,
String tag) {
noteWakupAlarmLocked(packageName, uid, workSource, tag,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWakupAlarmLocked(String packageName, int uid, WorkSource workSource,
String tag, long elapsedRealtimeMs, long uptimeMs) {
if (workSource != null) {
@@ -4481,6 +4531,7 @@
mExternalSync.scheduleCpuSyncDueToWakelockChange(0 /* delayMillis */);
}
+ @GuardedBy("this")
public void setRecordAllHistoryLocked(boolean enabled) {
mRecordAllHistory = enabled;
if (!enabled) {
@@ -4524,6 +4575,7 @@
mNoAutoReset = enabled;
}
+ @GuardedBy("this")
public void setPretendScreenOff(boolean pretendScreenOff) {
if (mPretendScreenOff != pretendScreenOff) {
mPretendScreenOff = pretendScreenOff;
@@ -4537,12 +4589,14 @@
private String mInitialAcquireWakeName;
private int mInitialAcquireWakeUid = -1;
+ @GuardedBy("this")
public void noteStartWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
int type, boolean unimportantForLogging) {
noteStartWakeLocked(uid, pid, wc, name, historyName, type, unimportantForLogging,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteStartWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
int type, boolean unimportantForLogging, long elapsedRealtimeMs, long uptimeMs) {
final int mappedUid = mapUid(uid);
@@ -4613,12 +4667,14 @@
}
}
+ @GuardedBy("this")
public void noteStopWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
int type) {
noteStopWakeLocked(uid, pid, wc, name, historyName, type,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteStopWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
int type, long elapsedRealtimeMs, long uptimeMs) {
final int mappedUid = mapUid(uid);
@@ -4701,12 +4757,14 @@
}
}
+ @GuardedBy("this")
public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, boolean unimportantForLogging) {
noteStartWakeFromSourceLocked(ws, pid, name, historyName, type, unimportantForLogging,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, boolean unimportantForLogging,
long elapsedRealtimeMs, long uptimeMs) {
@@ -4726,6 +4784,7 @@
}
}
+ @GuardedBy("this")
public void noteChangeWakelockFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, WorkSource newWs, int newPid, String newName,
String newHistoryName, int newType, boolean newUnimportantForLogging) {
@@ -4734,6 +4793,7 @@
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteChangeWakelockFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, WorkSource newWs, int newPid, String newName,
String newHistoryName, int newType, boolean newUnimportantForLogging,
@@ -4780,12 +4840,14 @@
}
}
+ @GuardedBy("this")
public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type) {
noteStopWakeFromSourceLocked(ws, pid, name, historyName, type,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, long elapsedRealtimeMs, long uptimeMs) {
final int N = ws.size();
@@ -4804,23 +4866,27 @@
}
}
+ @GuardedBy("this")
public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
noteLongPartialWakelockStart(name, historyName, uid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteLongPartialWakelockStart(String name, String historyName, int uid,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
noteLongPartialWakeLockStartInternal(name, historyName, uid, elapsedRealtimeMs, uptimeMs);
}
+ @GuardedBy("this")
public void noteLongPartialWakelockStartFromSource(String name, String historyName,
WorkSource workSource) {
noteLongPartialWakelockStartFromSource(name, historyName, workSource,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteLongPartialWakelockStartFromSource(String name, String historyName,
WorkSource workSource, long elapsedRealtimeMs, long uptimeMs) {
final int N = workSource.size();
@@ -4841,6 +4907,7 @@
}
}
+ @GuardedBy("this")
private void noteLongPartialWakeLockStartInternal(String name, String historyName, int uid,
long elapsedRealtimeMs, long uptimeMs) {
if (historyName == null) {
@@ -4854,23 +4921,27 @@
historyName, uid);
}
+ @GuardedBy("this")
public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
noteLongPartialWakelockFinish(name, historyName, uid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteLongPartialWakelockFinish(String name, String historyName, int uid,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
noteLongPartialWakeLockFinishInternal(name, historyName, uid, elapsedRealtimeMs, uptimeMs);
}
+ @GuardedBy("this")
public void noteLongPartialWakelockFinishFromSource(String name, String historyName,
WorkSource workSource) {
noteLongPartialWakelockFinishFromSource(name, historyName, workSource,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteLongPartialWakelockFinishFromSource(String name, String historyName,
WorkSource workSource, long elapsedRealtimeMs, long uptimeMs) {
final int N = workSource.size();
@@ -4891,6 +4962,7 @@
}
}
+ @GuardedBy("this")
private void noteLongPartialWakeLockFinishInternal(String name, String historyName, int uid,
long elapsedRealtimeMs, long uptimeMs) {
if (historyName == null) {
@@ -4904,6 +4976,7 @@
historyName, uid);
}
+ @GuardedBy("this")
void aggregateLastWakeupUptimeLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mLastWakeupReason != null) {
long deltaUptimeMs = uptimeMs - mLastWakeupUptimeMs;
@@ -4915,10 +4988,12 @@
}
}
+ @GuardedBy("this")
public void noteWakeupReasonLocked(String reason) {
noteWakeupReasonLocked(reason, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWakeupReasonLocked(String reason, long elapsedRealtimeMs, long uptimeMs) {
if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason \"" + reason +"\": "
+ Integer.toHexString(mHistoryCur.states));
@@ -4931,11 +5006,13 @@
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
+ @GuardedBy("this")
public boolean startAddingCpuLocked() {
mExternalSync.cancelCpuSyncDueToWakelockChange();
return mOnBatteryInternal;
}
+ @GuardedBy("this")
public void finishAddingCpuLocked(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
int statSoftIrqTimeMs, int statIdleTimeMs) {
@@ -4985,10 +5062,12 @@
int mSensorNesting;
+ @GuardedBy("this")
public void noteStartSensorLocked(int uid, int sensor) {
noteStartSensorLocked(uid, sensor, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteStartSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mSensorNesting == 0) {
@@ -5002,10 +5081,12 @@
.noteStartSensor(sensor, elapsedRealtimeMs);
}
+ @GuardedBy("this")
public void noteStopSensorLocked(int uid, int sensor) {
noteStopSensorLocked(uid, sensor, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteStopSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
mSensorNesting--;
@@ -5021,10 +5102,12 @@
int mGpsNesting;
+ @GuardedBy("this")
public void noteGpsChangedLocked(WorkSource oldWs, WorkSource newWs) {
noteGpsChangedLocked(oldWs, newWs, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteGpsChangedLocked(WorkSource oldWs, WorkSource newWs,
long elapsedRealtimeMs, long uptimeMs) {
for (int i = 0; i < newWs.size(); ++i) {
@@ -5053,6 +5136,7 @@
}
}
+ @GuardedBy("this")
private void noteStartGpsLocked(int uid, WorkChain workChain,
long elapsedRealtimeMs, long uptimeMs) {
uid = getAttributionUid(uid, workChain);
@@ -5076,6 +5160,7 @@
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs).noteStartGps(elapsedRealtimeMs);
}
+ @GuardedBy("this")
private void noteStopGpsLocked(int uid, WorkChain workChain,
long elapsedRealtimeMs, long uptimeMs) {
uid = getAttributionUid(uid, workChain);
@@ -5100,10 +5185,12 @@
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs).noteStopGps(elapsedRealtimeMs);
}
+ @GuardedBy("this")
public void noteGpsSignalQualityLocked(int signalLevel) {
noteGpsSignalQualityLocked(signalLevel, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteGpsSignalQualityLocked(int signalLevel, long elapsedRealtimeMs, long uptimeMs) {
if (mGpsNesting == 0) {
return;
@@ -5372,6 +5459,7 @@
}
@UnsupportedAppUsage
+ @GuardedBy("this")
public void noteScreenBrightnessLocked(int brightness) {
noteScreenBrightnessLocked(0, brightness);
}
@@ -5379,6 +5467,7 @@
/**
* Note screen brightness change for a display.
*/
+ @GuardedBy("this")
public void noteScreenBrightnessLocked(int display, int brightness) {
noteScreenBrightnessLocked(display, brightness, mClock.elapsedRealtime(),
mClock.uptimeMillis());
@@ -5388,6 +5477,7 @@
/**
* Note screen brightness change for a display.
*/
+ @GuardedBy("this")
public void noteScreenBrightnessLocked(int display, int brightness, long elapsedRealtimeMs,
long uptimeMs) {
// Bin the brightness.
@@ -5425,6 +5515,7 @@
maybeUpdateOverallScreenBrightness(overallBin, elapsedRealtimeMs, uptimeMs);
}
+ @GuardedBy("this")
private int evaluateOverallScreenBrightnessBinLocked() {
int overallBin = -1;
final int numDisplays = getDisplayCount();
@@ -5442,6 +5533,7 @@
return overallBin;
}
+ @GuardedBy("this")
private void maybeUpdateOverallScreenBrightness(int overallBin, long elapsedRealtimeMs,
long uptimeMs) {
if (mScreenBrightnessBin != overallBin) {
@@ -5469,10 +5561,12 @@
}
@UnsupportedAppUsage
+ @GuardedBy("this")
public void noteUserActivityLocked(int uid, int event) {
noteUserActivityLocked(uid, event, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteUserActivityLocked(int uid, int event, long elapsedRealtimeMs, long uptimeMs) {
if (mOnBatteryInternal) {
uid = mapUid(uid);
@@ -5480,20 +5574,24 @@
}
}
+ @GuardedBy("this")
public void noteWakeUpLocked(String reason, int reasonUid) {
noteWakeUpLocked(reason, reasonUid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWakeUpLocked(String reason, int reasonUid,
long elapsedRealtimeMs, long uptimeMs) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SCREEN_WAKE_UP,
reason, reasonUid);
}
+ @GuardedBy("this")
public void noteInteractiveLocked(boolean interactive) {
noteInteractiveLocked(interactive, mClock.elapsedRealtime());
}
+ @GuardedBy("this")
public void noteInteractiveLocked(boolean interactive, long elapsedRealtimeMs) {
if (mInteractive != interactive) {
mInteractive = interactive;
@@ -5506,11 +5604,13 @@
}
}
+ @GuardedBy("this")
public void noteConnectivityChangedLocked(int type, String extra) {
noteConnectivityChangedLocked(type, extra,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteConnectivityChangedLocked(int type, String extra,
long elapsedRealtimeMs, long uptimeMs) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_CONNECTIVITY_CHANGED,
@@ -5518,6 +5618,7 @@
mNumConnectivityChange++;
}
+ @GuardedBy("this")
private void noteMobileRadioApWakeupLocked(final long elapsedRealtimeMillis,
final long uptimeMillis, int uid) {
uid = mapUid(uid);
@@ -5529,11 +5630,13 @@
/**
* Updates the radio power state and returns true if an external stats collection should occur.
*/
+ @GuardedBy("this")
public boolean noteMobileRadioPowerStateLocked(int powerState, long timestampNs, int uid) {
return noteMobileRadioPowerStateLocked(powerState, timestampNs, uid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public boolean noteMobileRadioPowerStateLocked(int powerState, long timestampNs, int uid,
long elapsedRealtimeMs, long uptimeMs) {
if (mMobileRadioPowerState != powerState) {
@@ -5578,6 +5681,7 @@
return false;
}
+ @GuardedBy("this")
public void notePowerSaveModeLocked(boolean enabled) {
notePowerSaveModeLocked(enabled, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
@@ -5585,6 +5689,7 @@
/**
* Toggles the power save mode state.
*/
+ @GuardedBy("this")
public void notePowerSaveModeLockedInit(boolean enabled, long elapsedRealtimeMs,
long uptimeMs) {
if (mPowerSaveModeEnabled != enabled) {
@@ -5599,6 +5704,7 @@
}
}
+ @GuardedBy("this")
public void notePowerSaveModeLocked(boolean enabled, long elapsedRealtimeMs, long uptimeMs) {
if (mPowerSaveModeEnabled != enabled) {
int stepState = enabled ? STEP_LEVEL_MODE_POWER_SAVE : 0;
@@ -5624,11 +5730,13 @@
}
}
+ @GuardedBy("this")
public void noteDeviceIdleModeLocked(final int mode, String activeReason, int activeUid) {
noteDeviceIdleModeLocked(mode, activeReason, activeUid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteDeviceIdleModeLocked(final int mode, String activeReason, int activeUid,
long elapsedRealtimeMs, long uptimeMs) {
boolean nowIdling = mode == DEVICE_IDLE_MODE_DEEP;
@@ -5702,11 +5810,13 @@
}
}
+ @GuardedBy("this")
public void notePackageInstalledLocked(String pkgName, long versionCode) {
notePackageInstalledLocked(pkgName, versionCode,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void notePackageInstalledLocked(String pkgName, long versionCode,
long elapsedRealtimeMs, long uptimeMs) {
// XXX need to figure out what to do with long version codes.
@@ -5719,10 +5829,12 @@
addPackageChange(pc);
}
+ @GuardedBy("this")
public void notePackageUninstalledLocked(String pkgName) {
notePackageUninstalledLocked(pkgName, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void notePackageUninstalledLocked(String pkgName,
long elapsedRealtimeMs, long uptimeMs) {
addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
@@ -5740,10 +5852,12 @@
mDailyPackageChanges.add(pc);
}
+ @GuardedBy("this")
void stopAllGpsSignalQualityTimersLocked(int except) {
stopAllGpsSignalQualityTimersLocked(except, mClock.elapsedRealtime());
}
+ @GuardedBy("this")
void stopAllGpsSignalQualityTimersLocked(int except, long elapsedRealtimeMs) {
for (int i = 0; i < mGpsSignalQualityTimer.length; i++) {
if (i == except) {
@@ -5756,10 +5870,12 @@
}
@UnsupportedAppUsage
+ @GuardedBy("this")
public void notePhoneOnLocked() {
notePhoneOnLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void notePhoneOnLocked(long elapsedRealtimeMs, long uptimeMs) {
if (!mPhoneOn) {
mHistoryCur.states2 |= HistoryItem.STATE2_PHONE_IN_CALL_FLAG;
@@ -5772,10 +5888,12 @@
}
@UnsupportedAppUsage
+ @GuardedBy("this")
public void notePhoneOffLocked() {
notePhoneOffLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void notePhoneOffLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mPhoneOn) {
mHistoryCur.states2 &= ~HistoryItem.STATE2_PHONE_IN_CALL_FLAG;
@@ -5787,6 +5905,7 @@
}
}
+ @GuardedBy("this")
private void registerUsbStateReceiver(Context context) {
final IntentFilter usbStateFilter = new IntentFilter();
usbStateFilter.addAction(UsbManager.ACTION_USB_STATE);
@@ -5811,6 +5930,7 @@
}
}
+ @GuardedBy("this")
private void noteUsbConnectionStateLocked(boolean connected, long elapsedRealtimeMs,
long uptimeMs) {
int newState = connected ? USB_DATA_CONNECTED : USB_DATA_DISCONNECTED;
@@ -5825,6 +5945,7 @@
}
}
+ @GuardedBy("this")
void stopAllPhoneSignalStrengthTimersLocked(int except, long elapsedRealtimeMs) {
for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
if (i == except) {
@@ -5849,6 +5970,7 @@
return state;
}
+ @GuardedBy("this")
private void updateAllPhoneStateLocked(int state, int simState, int strengthBin,
long elapsedRealtimeMs, long uptimeMs) {
boolean scanning = false;
@@ -5945,10 +6067,12 @@
* Telephony stack updates the phone state.
* @param state phone state from ServiceState.getState()
*/
+ @GuardedBy("this")
public void notePhoneStateLocked(int state, int simState) {
notePhoneStateLocked(state, simState, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void notePhoneStateLocked(int state, int simState,
long elapsedRealtimeMs, long uptimeMs) {
updateAllPhoneStateLocked(state, simState, mPhoneSignalStrengthBinRaw,
@@ -5956,11 +6080,13 @@
}
@UnsupportedAppUsage
+ @GuardedBy("this")
public void notePhoneSignalStrengthLocked(SignalStrength signalStrength) {
notePhoneSignalStrengthLocked(signalStrength,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void notePhoneSignalStrengthLocked(SignalStrength signalStrength,
long elapsedRealtimeMs, long uptimeMs) {
// Bin the strength.
@@ -5970,11 +6096,13 @@
}
@UnsupportedAppUsage
+ @GuardedBy("this")
public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData, int serviceType) {
notePhoneDataConnectionStateLocked(dataType, hasData, serviceType,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData, int serviceType,
long elapsedRealtimeMs, long uptimeMs) {
// BatteryStats uses 0 to represent no network type.
@@ -6014,10 +6142,12 @@
}
}
+ @GuardedBy("this")
public void noteWifiOnLocked() {
noteWifiOnLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiOnLocked(long elapsedRealtimeMs, long uptimeMs) {
if (!mWifiOn) {
mHistoryCur.states2 |= HistoryItem.STATE2_WIFI_ON_FLAG;
@@ -6030,10 +6160,12 @@
}
}
+ @GuardedBy("this")
public void noteWifiOffLocked() {
noteWifiOffLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiOffLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mWifiOn) {
mHistoryCur.states2 &= ~HistoryItem.STATE2_WIFI_ON_FLAG;
@@ -6047,10 +6179,12 @@
}
@UnsupportedAppUsage
+ @GuardedBy("this")
public void noteAudioOnLocked(int uid) {
noteAudioOnLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteAudioOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mAudioOnNesting == 0) {
@@ -6066,10 +6200,12 @@
}
@UnsupportedAppUsage
+ @GuardedBy("this")
public void noteAudioOffLocked(int uid) {
noteAudioOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteAudioOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mAudioOnNesting == 0) {
return;
@@ -6087,10 +6223,12 @@
}
@UnsupportedAppUsage
+ @GuardedBy("this")
public void noteVideoOnLocked(int uid) {
noteVideoOnLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteVideoOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mVideoOnNesting == 0) {
@@ -6106,10 +6244,12 @@
}
@UnsupportedAppUsage
+ @GuardedBy("this")
public void noteVideoOffLocked(int uid) {
noteVideoOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteVideoOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mVideoOnNesting == 0) {
return;
@@ -6126,10 +6266,12 @@
.noteVideoTurnedOffLocked(elapsedRealtimeMs);
}
+ @GuardedBy("this")
public void noteResetAudioLocked() {
noteResetAudioLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteResetAudioLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mAudioOnNesting > 0) {
mAudioOnNesting = 0;
@@ -6145,10 +6287,12 @@
}
}
+ @GuardedBy("this")
public void noteResetVideoLocked() {
noteResetVideoLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteResetVideoLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mVideoOnNesting > 0) {
mVideoOnNesting = 0;
@@ -6164,31 +6308,37 @@
}
}
+ @GuardedBy("this")
public void noteActivityResumedLocked(int uid) {
noteActivityResumedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteActivityResumedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteActivityResumedLocked(elapsedRealtimeMs);
}
+ @GuardedBy("this")
public void noteActivityPausedLocked(int uid) {
noteActivityPausedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteActivityPausedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteActivityPausedLocked(elapsedRealtimeMs);
}
+ @GuardedBy("this")
public void noteVibratorOnLocked(int uid, long durationMillis) {
noteVibratorOnLocked(uid, durationMillis,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteVibratorOnLocked(int uid, long durationMillis,
long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
@@ -6196,20 +6346,24 @@
.noteVibratorOnLocked(durationMillis, elapsedRealtimeMs);
}
+ @GuardedBy("this")
public void noteVibratorOffLocked(int uid) {
noteVibratorOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteVibratorOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteVibratorOffLocked(elapsedRealtimeMs);
}
+ @GuardedBy("this")
public void noteFlashlightOnLocked(int uid) {
noteFlashlightOnLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteFlashlightOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mFlashlightOnNesting++ == 0) {
@@ -6223,10 +6377,12 @@
.noteFlashlightTurnedOnLocked(elapsedRealtimeMs);
}
+ @GuardedBy("this")
public void noteFlashlightOffLocked(int uid) {
noteFlashlightOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteFlashlightOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mFlashlightOnNesting == 0) {
return;
@@ -6243,10 +6399,12 @@
.noteFlashlightTurnedOffLocked(elapsedRealtimeMs);
}
+ @GuardedBy("this")
public void noteCameraOnLocked(int uid) {
noteCameraOnLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteCameraOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mCameraOnNesting++ == 0) {
@@ -6260,10 +6418,12 @@
.noteCameraTurnedOnLocked(elapsedRealtimeMs);
}
+ @GuardedBy("this")
public void noteCameraOffLocked(int uid) {
noteCameraOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteCameraOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mCameraOnNesting == 0) {
return;
@@ -6280,10 +6440,12 @@
.noteCameraTurnedOffLocked(elapsedRealtimeMs);
}
+ @GuardedBy("this")
public void noteResetCameraLocked() {
noteResetCameraLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteResetCameraLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mCameraOnNesting > 0) {
mCameraOnNesting = 0;
@@ -6299,10 +6461,12 @@
}
}
+ @GuardedBy("this")
public void noteResetFlashlightLocked() {
noteResetFlashlightLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteResetFlashlightLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mFlashlightOnNesting > 0) {
mFlashlightOnNesting = 0;
@@ -6318,6 +6482,7 @@
}
}
+ @GuardedBy("this")
private void noteBluetoothScanStartedLocked(WorkChain workChain, int uid,
boolean isUnoptimized, long elapsedRealtimeMs, long uptimeMs) {
uid = getAttributionUid(uid, workChain);
@@ -6333,11 +6498,13 @@
.noteBluetoothScanStartedLocked(elapsedRealtimeMs, isUnoptimized);
}
+ @GuardedBy("this")
public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws, boolean isUnoptimized) {
noteBluetoothScanStartedFromSourceLocked(ws, isUnoptimized,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws, boolean isUnoptimized,
long elapsedRealtimeMs, long uptimeMs) {
final int N = ws.size();
@@ -6355,6 +6522,7 @@
}
}
+ @GuardedBy("this")
private void noteBluetoothScanStoppedLocked(WorkChain workChain, int uid,
boolean isUnoptimized, long elapsedRealtimeMs, long uptimeMs) {
uid = getAttributionUid(uid, workChain);
@@ -6378,11 +6546,13 @@
return mapUid(uid);
}
+ @GuardedBy("this")
public void noteBluetoothScanStoppedFromSourceLocked(WorkSource ws, boolean isUnoptimized) {
noteBluetoothScanStoppedFromSourceLocked(ws, isUnoptimized,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteBluetoothScanStoppedFromSourceLocked(WorkSource ws, boolean isUnoptimized,
long elapsedRealtimeMs, long uptimeMs) {
final int N = ws.size();
@@ -6400,10 +6570,12 @@
}
}
+ @GuardedBy("this")
public void noteResetBluetoothScanLocked() {
noteResetBluetoothScanLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteResetBluetoothScanLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mBluetoothScanNesting > 0) {
mBluetoothScanNesting = 0;
@@ -6419,11 +6591,13 @@
}
}
+ @GuardedBy("this")
public void noteBluetoothScanResultsFromSourceLocked(WorkSource ws, int numNewResults) {
noteBluetoothScanResultsFromSourceLocked(ws, numNewResults,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteBluetoothScanResultsFromSourceLocked(WorkSource ws, int numNewResults,
long elapsedRealtimeMs, long uptimeMs) {
final int N = ws.size();
@@ -6444,6 +6618,7 @@
}
}
+ @GuardedBy("this")
private void noteWifiRadioApWakeupLocked(final long elapsedRealtimeMillis,
final long uptimeMillis, int uid) {
uid = mapUid(uid);
@@ -6452,11 +6627,13 @@
getUidStatsLocked(uid, elapsedRealtimeMillis, uptimeMillis).noteWifiRadioApWakeupLocked();
}
+ @GuardedBy("this")
public void noteWifiRadioPowerState(int powerState, long timestampNs, int uid) {
noteWifiRadioPowerState(powerState, timestampNs, uid,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiRadioPowerState(int powerState, long timestampNs, int uid,
long elapsedRealtimeMs, long uptimeMs) {
if (mWifiRadioPowerState != powerState) {
@@ -6480,10 +6657,12 @@
}
}
+ @GuardedBy("this")
public void noteWifiRunningLocked(WorkSource ws) {
noteWifiRunningLocked(ws, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiRunningLocked(WorkSource ws, long elapsedRealtimeMs, long uptimeMs) {
if (!mGlobalWifiRunning) {
mHistoryCur.states2 |= HistoryItem.STATE2_WIFI_RUNNING_FLAG;
@@ -6514,11 +6693,13 @@
}
}
+ @GuardedBy("this")
public void noteWifiRunningChangedLocked(WorkSource oldWs, WorkSource newWs) {
noteWifiRunningChangedLocked(oldWs, newWs,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiRunningChangedLocked(WorkSource oldWs, WorkSource newWs,
long elapsedRealtimeMs, long uptimeMs) {
if (mGlobalWifiRunning) {
@@ -6558,10 +6739,12 @@
}
}
+ @GuardedBy("this")
public void noteWifiStoppedLocked(WorkSource ws) {
noteWifiStoppedLocked(ws, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiStoppedLocked(WorkSource ws, long elapsedRealtimeMs, long uptimeMs) {
if (mGlobalWifiRunning) {
mHistoryCur.states2 &= ~HistoryItem.STATE2_WIFI_RUNNING_FLAG;
@@ -6592,10 +6775,12 @@
}
}
+ @GuardedBy("this")
public void noteWifiStateLocked(int wifiState, String accessPoint) {
noteWifiStateLocked(wifiState, accessPoint, mClock.elapsedRealtime());
}
+ @GuardedBy("this")
public void noteWifiStateLocked(int wifiState, String accessPoint, long elapsedRealtimeMs) {
if (DEBUG) Log.i(TAG, "WiFi state -> " + wifiState);
if (mWifiState != wifiState) {
@@ -6608,11 +6793,13 @@
}
}
+ @GuardedBy("this")
public void noteWifiSupplicantStateChangedLocked(int supplState, boolean failedAuth) {
noteWifiSupplicantStateChangedLocked(supplState, failedAuth,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiSupplicantStateChangedLocked(int supplState, boolean failedAuth,
long elapsedRealtimeMs, long uptimeMs) {
if (DEBUG) Log.i(TAG, "WiFi suppl state -> " + supplState);
@@ -6631,6 +6818,7 @@
}
}
+ @GuardedBy("this")
void stopAllWifiSignalStrengthTimersLocked(int except, long elapsedRealtimeMs) {
for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
if (i == except) {
@@ -6642,10 +6830,12 @@
}
}
+ @GuardedBy("this")
public void noteWifiRssiChangedLocked(int newRssi) {
noteWifiRssiChangedLocked(newRssi, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiRssiChangedLocked(int newRssi, long elapsedRealtimeMs, long uptimeMs) {
int strengthBin = WifiManager.calculateSignalLevel(newRssi, NUM_WIFI_SIGNAL_STRENGTH_BINS);
if (DEBUG) Log.i(TAG, "WiFi rssi -> " + newRssi + " bin=" + strengthBin);
@@ -6674,10 +6864,12 @@
int mWifiFullLockNesting = 0;
@UnsupportedAppUsage
+ @GuardedBy("this")
public void noteFullWifiLockAcquiredLocked(int uid) {
noteFullWifiLockAcquiredLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteFullWifiLockAcquiredLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mWifiFullLockNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
@@ -6691,10 +6883,12 @@
}
@UnsupportedAppUsage
+ @GuardedBy("this")
public void noteFullWifiLockReleasedLocked(int uid) {
noteFullWifiLockReleasedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteFullWifiLockReleasedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
mWifiFullLockNesting--;
if (mWifiFullLockNesting == 0) {
@@ -6709,10 +6903,12 @@
int mWifiScanNesting = 0;
+ @GuardedBy("this")
public void noteWifiScanStartedLocked(int uid) {
noteWifiScanStartedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiScanStartedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mWifiScanNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_FLAG;
@@ -6725,10 +6921,12 @@
.noteWifiScanStartedLocked(elapsedRealtimeMs);
}
+ @GuardedBy("this")
public void noteWifiScanStoppedLocked(int uid) {
noteWifiScanStoppedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiScanStoppedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
mWifiScanNesting--;
if (mWifiScanNesting == 0) {
@@ -6765,11 +6963,13 @@
int mWifiMulticastNesting = 0;
+ @GuardedBy("this")
@UnsupportedAppUsage
public void noteWifiMulticastEnabledLocked(int uid) {
noteWifiMulticastEnabledLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiMulticastEnabledLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mWifiMulticastNesting == 0) {
@@ -6790,10 +6990,12 @@
}
@UnsupportedAppUsage
+ @GuardedBy("this")
public void noteWifiMulticastDisabledLocked(int uid) {
noteWifiMulticastDisabledLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiMulticastDisabledLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
mWifiMulticastNesting--;
@@ -6813,11 +7015,13 @@
.noteWifiMulticastDisabledLocked(elapsedRealtimeMs);
}
+ @GuardedBy("this")
public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws) {
noteFullWifiLockAcquiredFromSourceLocked(ws,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws,
long elapsedRealtimeMs, long uptimeMs) {
int N = ws.size();
@@ -6836,11 +7040,13 @@
}
}
+ @GuardedBy("this")
public void noteFullWifiLockReleasedFromSourceLocked(WorkSource ws) {
noteFullWifiLockReleasedFromSourceLocked(ws,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteFullWifiLockReleasedFromSourceLocked(WorkSource ws,
long elapsedRealtimeMs, long uptimeMs) {
int N = ws.size();
@@ -6859,10 +7065,12 @@
}
}
+ @GuardedBy("this")
public void noteWifiScanStartedFromSourceLocked(WorkSource ws) {
noteWifiScanStartedFromSourceLocked(ws, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiScanStartedFromSourceLocked(WorkSource ws,
long elapsedRealtimeMs, long uptimeMs) {
int N = ws.size();
@@ -6881,10 +7089,12 @@
}
}
+ @GuardedBy("this")
public void noteWifiScanStoppedFromSourceLocked(WorkSource ws) {
noteWifiScanStoppedFromSourceLocked(ws, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiScanStoppedFromSourceLocked(WorkSource ws,
long elapsedRealtimeMs, long uptimeMs) {
int N = ws.size();
@@ -6903,11 +7113,13 @@
}
}
+ @GuardedBy("this")
public void noteWifiBatchedScanStartedFromSourceLocked(WorkSource ws, int csph) {
noteWifiBatchedScanStartedFromSourceLocked(ws, csph,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+ @GuardedBy("this")
public void noteWifiBatchedScanStartedFromSourceLocked(WorkSource ws, int csph,
long elapsedRealtimeMs, long uptimeMs) {
int N = ws.size();
@@ -6924,10 +7136,13 @@
}
}
+ @GuardedBy("this")
public void noteWifiBatchedScanStoppedFromSourceLocked(WorkSource ws) {
noteWifiBatchedScanStoppedFromSourceLocked(ws,
mClock.elapsedRealtime(), mClock.uptimeMillis());
}
+
+ @GuardedBy("this")
public void noteWifiBatchedScanStoppedFromSourceLocked(WorkSource ws,
long elapsedRealtimeMs, long uptimeMs) {
int N = ws.size();
@@ -7454,36 +7669,43 @@
}
}
+ @GuardedBy("this")
@Override
public long getBluetoothMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH);
}
+ @GuardedBy("this")
@Override
public long getCpuMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
}
+ @GuardedBy("this")
@Override
public long getGnssMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_GNSS);
}
+ @GuardedBy("this")
@Override
public long getMobileRadioMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO);
}
+ @GuardedBy("this")
@Override
public long getScreenOnMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
}
+ @GuardedBy("this")
@Override
public long getScreenDozeMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE);
}
+ @GuardedBy("this")
@Override
public long getWifiMeasuredBatteryConsumptionUC() {
return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI);
@@ -7496,6 +7718,7 @@
* @param bucket standard power bucket of interest
* @return charge (in microcoulombs) used for this power bucket
*/
+ @GuardedBy("this")
private long getPowerBucketConsumptionUC(@StandardPowerBucket int bucket) {
if (mGlobalMeasuredEnergyStats == null) {
return POWER_DATA_UNAVAILABLE;
@@ -7503,6 +7726,7 @@
return mGlobalMeasuredEnergyStats.getAccumulatedStandardBucketCharge(bucket);
}
+ @GuardedBy("this")
@Override
public @Nullable long[] getCustomConsumerMeasuredBatteryConsumptionUC() {
if (mGlobalMeasuredEnergyStats == null) {
@@ -7514,12 +7738,13 @@
/**
* Returns the names of custom power components.
*/
+ @GuardedBy("this")
@Override
public @NonNull String[] getCustomEnergyConsumerNames() {
- if (mGlobalMeasuredEnergyStats == null) {
+ if (mMeasuredEnergyStatsConfig == null) {
return new String[0];
}
- final String[] names = mGlobalMeasuredEnergyStats.getCustomBucketNames();
+ final String[] names = mMeasuredEnergyStatsConfig.getCustomBucketNames();
for (int i = 0; i < names.length; i++) {
if (TextUtils.isEmpty(names[i])) {
names[i] = "CUSTOM_" + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i;
@@ -7528,6 +7753,7 @@
return names;
}
+ @GuardedBy("this")
@Override public long getStartClockTime() {
final long currentTimeMs = mClock.currentTimeMillis();
if ((currentTimeMs > MILLISECONDS_IN_YEAR
@@ -7954,11 +8180,18 @@
mJobsFreshnessBuckets = new Counter[JOB_FRESHNESS_BUCKETS.length];
}
+ @GuardedBy("mBsi")
@VisibleForTesting
public void setProcessStateForTest(int procState, long elapsedTimeMs) {
mProcessState = procState;
getProcStateTimeCounter().setState(procState, elapsedTimeMs);
getProcStateScreenOffTimeCounter().setState(procState, elapsedTimeMs);
+ final MeasuredEnergyStats energyStats =
+ getOrCreateMeasuredEnergyStatsIfSupportedLocked();
+ if (energyStats != null) {
+ energyStats.setState(mapUidProcessStateToBatteryConsumerProcessState(procState),
+ elapsedTimeMs);
+ }
}
@Override
@@ -7981,6 +8214,7 @@
return nullIfAllZeros(mCpuClusterTimesMs, STATS_SINCE_CHARGED);
}
+ @GuardedBy("mBsi")
@Override
public long[] getCpuFreqTimes(int which, int procState) {
if (procState < 0 || procState >= NUM_PROCESS_STATE) {
@@ -7997,6 +8231,7 @@
return mProcStateTimeMs.getCountsLocked(which, procState);
}
+ @GuardedBy("mBsi")
@Override
public long[] getScreenOffCpuFreqTimes(int which, int procState) {
if (procState < 0 || procState >= NUM_PROCESS_STATE) {
@@ -8026,6 +8261,7 @@
return mProportionalSystemServiceUsage;
}
+ @GuardedBy("mBsi")
public void addIsolatedUid(int isolatedUid) {
if (mChildUids == null) {
mChildUids = new SparseArray<>();
@@ -8312,23 +8548,37 @@
return mModemControllerActivity;
}
+ @GuardedBy("mBsi")
private MeasuredEnergyStats getOrCreateMeasuredEnergyStatsLocked() {
if (mUidMeasuredEnergyStats == null) {
- mUidMeasuredEnergyStats =
- MeasuredEnergyStats.createFromTemplate(mBsi.mGlobalMeasuredEnergyStats);
+ mUidMeasuredEnergyStats = new MeasuredEnergyStats(mBsi.mMeasuredEnergyStatsConfig);
+ }
+ return mUidMeasuredEnergyStats;
+ }
+
+ @GuardedBy("mBsi")
+ private MeasuredEnergyStats getOrCreateMeasuredEnergyStatsIfSupportedLocked() {
+ if (mUidMeasuredEnergyStats == null && mBsi.mMeasuredEnergyStatsConfig != null) {
+ mUidMeasuredEnergyStats = new MeasuredEnergyStats(mBsi.mMeasuredEnergyStatsConfig);
}
return mUidMeasuredEnergyStats;
}
/** Adds the given charge to the given standard power bucket for this uid. */
+ @GuardedBy("mBsi")
private void addChargeToStandardBucketLocked(long chargeDeltaUC,
@StandardPowerBucket int powerBucket) {
- getOrCreateMeasuredEnergyStatsLocked().updateStandardBucket(powerBucket, chargeDeltaUC);
+ final MeasuredEnergyStats measuredEnergyStats =
+ getOrCreateMeasuredEnergyStatsLocked();
+ measuredEnergyStats.updateStandardBucket(powerBucket, chargeDeltaUC,
+ mBsi.mClock.elapsedRealtime());
}
/** Adds the given charge to the given custom power bucket for this uid. */
+ @GuardedBy("mBsi")
private void addChargeToCustomBucketLocked(long chargeDeltaUC, int powerBucket) {
- getOrCreateMeasuredEnergyStatsLocked().updateCustomBucket(powerBucket, chargeDeltaUC);
+ getOrCreateMeasuredEnergyStatsLocked().updateCustomBucket(powerBucket, chargeDeltaUC,
+ mBsi.mClock.elapsedRealtime());
}
/**
@@ -8337,6 +8587,7 @@
* @param bucket standard power bucket of interest
* @return consumption (in microcolombs) used by this uid for this power bucket
*/
+ @GuardedBy("mBsi")
public long getMeasuredBatteryConsumptionUC(@StandardPowerBucket int bucket) {
if (mBsi.mGlobalMeasuredEnergyStats == null
|| !mBsi.mGlobalMeasuredEnergyStats.isStandardBucketSupported(bucket)) {
@@ -8348,6 +8599,7 @@
return mUidMeasuredEnergyStats.getAccumulatedStandardBucketCharge(bucket);
}
+ @GuardedBy("mBsi")
@Override
public long[] getCustomConsumerMeasuredBatteryConsumptionUC() {
if (mBsi.mGlobalMeasuredEnergyStats == null) {
@@ -8360,31 +8612,37 @@
return mUidMeasuredEnergyStats.getAccumulatedCustomBucketCharges();
}
+ @GuardedBy("mBsi")
@Override
public long getBluetoothMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH);
}
+ @GuardedBy("mBsi")
@Override
public long getCpuMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
}
+ @GuardedBy("mBsi")
@Override
public long getGnssMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_GNSS);
}
+ @GuardedBy("mBsi")
@Override
public long getMobileRadioMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO);
}
+ @GuardedBy("mBsi")
@Override
public long getScreenOnMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
}
+ @GuardedBy("mBsi")
@Override
public long getWifiMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI);
@@ -9678,6 +9936,7 @@
}
}
+ @GuardedBy("mBsi")
void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
mOnBatteryBackgroundTimeBase.readFromParcel(in);
mOnBatteryScreenOffBackgroundTimeBase.readFromParcel(in);
@@ -9917,7 +10176,8 @@
}
if (in.readInt() != 0) {
- mUidMeasuredEnergyStats = new MeasuredEnergyStats(in);
+ mUidMeasuredEnergyStats = new MeasuredEnergyStats(mBsi.mMeasuredEnergyStatsConfig,
+ in);
}
mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
@@ -10801,6 +11061,7 @@
mBsi.mClock.elapsedRealtime(), mBsi.mClock.uptimeMillis());
}
+ @GuardedBy("mBsi")
public void updateUidProcessStateLocked(int procState,
long elapsedRealtimeMs, long uptimeMs) {
int uidRunningState;
@@ -10845,6 +11106,14 @@
updateOnBatteryBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
updateOnBatteryScreenOffBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
+
+ final MeasuredEnergyStats energyStats =
+ getOrCreateMeasuredEnergyStatsIfSupportedLocked();
+ if (energyStats != null) {
+ energyStats.setState(
+ mapUidProcessStateToBatteryConsumerProcessState(mProcessState),
+ elapsedRealtimeMs);
+ }
}
if (userAwareService != mInForegroundService) {
@@ -11485,6 +11754,7 @@
}
}
+ @GuardedBy("this")
public void readDailyStatsLocked() {
Slog.d(TAG, "Reading daily items from " + mDailyFile.getBaseFile());
mDailyItems.clear();
@@ -11659,6 +11929,7 @@
return mNextMaxDailyDeadlineMs;
}
+ @GuardedBy("this")
public int getHistoryTotalSize() {
return mConstants.MAX_HISTORY_BUFFER * mConstants.MAX_HISTORY_FILES;
}
@@ -11779,6 +12050,7 @@
mBatteryResetListener = batteryResetListener;
}
+ @GuardedBy("this")
public void resetAllStatsCmdLocked() {
final long mSecUptime = mClock.uptimeMillis();
long uptimeUs = mSecUptime * 1000;
@@ -11813,6 +12085,7 @@
initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
}
+ @GuardedBy("this")
private void resetAllStatsLocked(long uptimeMillis, long elapsedRealtimeMillis,
int resetReason) {
if (mBatteryResetListener != null) {
@@ -11971,6 +12244,7 @@
mHandler.sendEmptyMessage(MSG_REPORT_RESET_STATS);
}
+ @GuardedBy("this")
private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) {
for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
if (!mRecordAllHistory && i == HistoryItem.EVENT_PROC) {
@@ -11991,11 +12265,13 @@
}
}
+ @GuardedBy("this")
void updateDischargeScreenLevelsLocked(int oldState, int newState) {
updateOldDischargeScreenLevelLocked(oldState);
updateNewDischargeScreenLevelLocked(newState);
}
+ @GuardedBy("this")
private void updateOldDischargeScreenLevelLocked(int state) {
if (Display.isOnState(state)) {
int diff = mDischargeScreenOnUnplugLevel - mDischargeCurrentLevel;
@@ -12018,6 +12294,7 @@
}
}
+ @GuardedBy("this")
private void updateNewDischargeScreenLevelLocked(int state) {
if (Display.isOnState(state)) {
mDischargeScreenOnUnplugLevel = mDischargeCurrentLevel;
@@ -12034,6 +12311,7 @@
}
}
+ @GuardedBy("this")
public void pullPendingStateUpdatesLocked() {
if (mOnBatteryInternal) {
updateDischargeScreenLevelsLocked(mScreenState, mScreenState);
@@ -12080,10 +12358,13 @@
* Distribute WiFi energy info and network traffic to apps.
* @param info The energy information from the WiFi controller.
*/
+ @GuardedBy("this")
public void updateWifiState(@Nullable final WifiActivityEnergyInfo info,
final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
if (DEBUG_ENERGY) {
- Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces));
+ synchronized (mWifiNetworkLock) {
+ Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces));
+ }
}
// Grab a separate lock to acquire the network stats, which may do I/O.
@@ -12714,7 +12995,7 @@
rxTimeMs = info.getControllerRxTimeMillis();
txTimeMs = info.getControllerTxTimeMillis();
energy = info.getControllerEnergyUsed();
- if (info.getUidTraffic() != null) {
+ if (!info.getUidTraffic().isEmpty()) {
for (UidTraffic traffic : info.getUidTraffic()) {
uidRxBytes.put(traffic.getUid(), traffic.getRxBytes());
uidTxBytes.put(traffic.getUid(), traffic.getTxBytes());
@@ -12740,6 +13021,7 @@
*
* @param info The accumulated energy information from the bluetooth controller.
*/
+ @GuardedBy("this")
public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info,
final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
if (DEBUG_ENERGY) {
@@ -12865,10 +13147,10 @@
long totalTxBytes = 0;
long totalRxBytes = 0;
- final UidTraffic[] uidTraffic = info.getUidTraffic();
- final int numUids = uidTraffic != null ? uidTraffic.length : 0;
+ final List<UidTraffic> uidTraffic = info.getUidTraffic();
+ final int numUids = uidTraffic.size();
for (int i = 0; i < numUids; i++) {
- final UidTraffic traffic = uidTraffic[i];
+ final UidTraffic traffic = uidTraffic.get(i);
final long rxBytes = traffic.getRxBytes() - mLastBluetoothActivityInfo.uidRxBytes.get(
traffic.getUid());
final long txBytes = traffic.getTxBytes() - mLastBluetoothActivityInfo.uidTxBytes.get(
@@ -12891,7 +13173,7 @@
if ((totalTxBytes != 0 || totalRxBytes != 0) && (leftOverRxTimeMs != 0
|| leftOverTxTimeMs != 0)) {
for (int i = 0; i < numUids; i++) {
- final UidTraffic traffic = uidTraffic[i];
+ final UidTraffic traffic = uidTraffic.get(i);
final int uid = traffic.getUid();
final long rxBytes =
traffic.getRxBytes() - mLastBluetoothActivityInfo.uidRxBytes.get(uid);
@@ -13039,6 +13321,7 @@
* clusterChargeUC against.
*/
@GuardedBy("this")
+ @SuppressWarnings("GuardedBy") // errorprone false positive on u.addChargeToStandardBucketLocked
private void updateCpuMeasuredEnergyStatsLocked(@NonNull long[] clusterChargeUC,
@NonNull CpuDeltaPowerAccumulator accumulator) {
if (DEBUG_ENERGY) {
@@ -13057,7 +13340,7 @@
if (totalCpuChargeUC <= 0) return;
mGlobalMeasuredEnergyStats.updateStandardBucket(MeasuredEnergyStats.POWER_BUCKET_CPU,
- totalCpuChargeUC);
+ totalCpuChargeUC, mClock.elapsedRealtime());
// Calculate the measured microcoulombs/calculated milliamp-hour charge ratio for each
// cluster to normalize each uid's estimated power usage against actual power usage for
@@ -13261,6 +13544,8 @@
* Data inside uidCharges will not be modified (treated immutable).
* Uids not already known to BatteryStats will be ignored.
*/
+ @GuardedBy("this")
+ @SuppressWarnings("GuardedBy") // errorprone false positive on u.addChargeToCustomBucketLocked
public void updateCustomMeasuredEnergyStatsLocked(int customPowerBucket,
long totalChargeUC, @Nullable SparseLongArray uidCharges) {
if (DEBUG_ENERGY) {
@@ -13272,7 +13557,8 @@
if (mGlobalMeasuredEnergyStats == null) return;
if (!mOnBatteryInternal || mIgnoreNextExternalStats || totalChargeUC <= 0) return;
- mGlobalMeasuredEnergyStats.updateCustomBucket(customPowerBucket, totalChargeUC);
+ mGlobalMeasuredEnergyStats.updateCustomBucket(customPowerBucket, totalChargeUC,
+ mClock.elapsedRealtime());
if (uidCharges == null) return;
final int numUids = uidCharges.size();
@@ -13280,6 +13566,7 @@
final int uidInt = mapUid(uidCharges.keyAt(i));
final long uidChargeUC = uidCharges.valueAt(i);
if (uidChargeUC == 0) continue;
+
final Uid uidObj = getAvailableUidStatsLocked(uidInt);
if (uidObj != null) {
uidObj.addChargeToCustomBucketLocked(uidChargeUC, customPowerBucket);
@@ -13314,6 +13601,8 @@
*
* <p>All uids in ratioNumerators must exist in mUidStats already.
*/
+ @GuardedBy("this")
+ @SuppressWarnings("GuardedBy") // errorprone false positive on u.addChargeToStandardBucketLocked
private void distributeEnergyToUidsLocked(@StandardPowerBucket int bucket,
long totalConsumedChargeUC, SparseDoubleArray ratioNumerators,
double minRatioDenominator) {
@@ -14256,6 +14545,7 @@
}
}
+ @GuardedBy("this")
private void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs,
boolean reset) {
mRecordingHistory = true;
@@ -14269,6 +14559,7 @@
}
}
+ @GuardedBy("this")
private void recordCurrentTimeChangeLocked(final long currentTimeMs,
final long elapsedRealtimeMs, final long uptimeMs) {
if (mRecordingHistory) {
@@ -14278,6 +14569,7 @@
}
}
+ @GuardedBy("this")
private void recordShutdownLocked(final long currentTimeMs, final long elapsedRealtimeMs) {
if (mRecordingHistory) {
mHistoryCur.currentTime = currentTimeMs;
@@ -14304,6 +14596,7 @@
mClock.elapsedRealtime(), mClock.uptimeMillis(), mClock.currentTimeMillis());
}
+ @GuardedBy("this")
public void setBatteryStateLocked(final int status, final int health, final int plugType,
final int level, /* not final */ int temp, final int voltageMv, final int chargeUah,
final int chargeFullUah, final long chargeTimeToFullSeconds,
@@ -14987,6 +15280,7 @@
return u;
}
+ @GuardedBy("this")
public void onCleanupUserLocked(int userId, long elapsedRealtimeMs) {
final int firstUidForUser = UserHandle.getUid(userId, 0);
final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1);
@@ -14994,6 +15288,7 @@
new UidToRemove(firstUidForUser, lastUidForUser, elapsedRealtimeMs));
}
+ @GuardedBy("this")
public void onUserRemovedLocked(int userId) {
if (mExternalSync != null) {
// Clear out the removed user's UIDs after a short delay. The delay is needed
@@ -15006,6 +15301,7 @@
/**
* Removes battery stats for UIDs corresponding to a removed user.
*/
+ @GuardedBy("this")
public void clearRemovedUserUidsLocked(int userId) {
final int firstUidForUser = UserHandle.getUid(userId, 0);
final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1);
@@ -15027,6 +15323,7 @@
* Remove the statistics object for a particular uid.
*/
@UnsupportedAppUsage
+ @GuardedBy("this")
public void removeUidStatsLocked(int uid) {
removeUidStatsLocked(uid, mClock.elapsedRealtime());
}
@@ -15034,6 +15331,7 @@
/**
* @see #removeUidStatsLocked(int)
*/
+ @GuardedBy("this")
public void removeUidStatsLocked(int uid, long elapsedRealtimeMs) {
final Uid u = mUidStats.get(uid);
if (u != null) {
@@ -15131,21 +15429,25 @@
return u.getServiceStatsLocked(pkg, name);
}
+ @GuardedBy("this")
public void shutdownLocked() {
recordShutdownLocked(mClock.currentTimeMillis(), mClock.elapsedRealtime());
writeSyncLocked();
mShuttingDown = true;
}
+ @GuardedBy("this")
@Override
public boolean isProcessStateDataAvailable() {
return trackPerProcStateCpuTimes();
}
+ @GuardedBy("this")
public boolean trackPerProcStateCpuTimes() {
return mConstants.TRACK_CPU_TIMES_BY_PROC_STATE && mPerProcStateCpuTimesAvailable;
}
+ @GuardedBy("this")
public void systemServicesReady(Context context) {
mConstants.startObserving(context.getContentResolver());
registerUsbStateReceiver(context);
@@ -15162,28 +15464,28 @@
@GuardedBy("this")
public void initMeasuredEnergyStatsLocked(@Nullable boolean[] supportedStandardBuckets,
String[] customBucketNames) {
- boolean supportedBucketMismatch = false;
-
final int numDisplays = mPerDisplayBatteryStats.length;
for (int i = 0; i < numDisplays; i++) {
final int screenState = mPerDisplayBatteryStats[i].screenState;
mPerDisplayBatteryStats[i].screenStateAtLastEnergyMeasurement = screenState;
}
- if (supportedStandardBuckets == null) {
- if (mGlobalMeasuredEnergyStats != null) {
- // Measured energy no longer supported, wipe out the existing data.
- supportedBucketMismatch = true;
- }
- } else {
- if (mGlobalMeasuredEnergyStats == null) {
- mGlobalMeasuredEnergyStats =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
+ final boolean compatibleConfig;
+ if (supportedStandardBuckets != null) {
+ final MeasuredEnergyStats.Config config = new MeasuredEnergyStats.Config(
+ supportedStandardBuckets, customBucketNames,
+ SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS,
+ getBatteryConsumerProcessStateNames());
+
+ if (mMeasuredEnergyStatsConfig == null) {
+ compatibleConfig = true;
} else {
- supportedBucketMismatch = !mGlobalMeasuredEnergyStats.isSupportEqualTo(
- supportedStandardBuckets, customBucketNames);
+ compatibleConfig = mMeasuredEnergyStatsConfig.isCompatible(config);
}
+ mMeasuredEnergyStatsConfig = config;
+ mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(config);
+
if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH]) {
mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
}
@@ -15196,11 +15498,14 @@
if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_WIFI]) {
mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
}
+ } else {
+ compatibleConfig = (mMeasuredEnergyStatsConfig == null);
+ // Measured energy no longer supported, wipe out the existing data.
+ mMeasuredEnergyStatsConfig = null;
+ mGlobalMeasuredEnergyStats = null;
}
- if (supportedBucketMismatch) {
- mGlobalMeasuredEnergyStats = supportedStandardBuckets == null
- ? null : new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
+ if (!compatibleConfig) {
// Supported power buckets changed since last boot.
// Existing data is no longer reliable.
resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
@@ -15208,6 +15513,16 @@
}
}
+ @NonNull
+ private static String[] getBatteryConsumerProcessStateNames() {
+ String[] procStateNames = new String[BatteryConsumer.PROCESS_STATE_COUNT];
+ for (int procState = 0; procState < BatteryConsumer.PROCESS_STATE_COUNT; procState++) {
+ procStateNames[procState] = BatteryConsumer.processStateToString(procState);
+ }
+ procStateNames[BatteryConsumer.PROCESS_STATE_ANY] = "untracked";
+ return procStateNames;
+ }
+
/** Get the last known Battery voltage (in millivolts), returns -1 if unknown */
@GuardedBy("this")
public int getBatteryVoltageMvLocked() {
@@ -15373,6 +15688,7 @@
}
}
+ @GuardedBy("BatteryStatsImpl.this")
private void updateProcStateCpuTimesReadDelayMs(long oldDelayMillis, long newDelayMillis) {
PROC_STATE_CPU_TIMES_READ_DELAY_MS = newDelayMillis;
if (oldDelayMillis != newDelayMillis) {
@@ -15393,6 +15709,7 @@
}
}
+ @GuardedBy("BatteryStatsImpl.this")
private void updateUidRemoveDelay(long newTimeMs) {
UID_REMOVE_DELAY_MS = newTimeMs;
clearPendingRemovedUidsLocked();
@@ -15530,16 +15847,19 @@
final ReentrantLock mWriteLock = new ReentrantLock();
+ @GuardedBy("this")
public void writeAsyncLocked() {
writeStatsLocked(false);
writeHistoryLocked(false);
}
+ @GuardedBy("this")
public void writeSyncLocked() {
writeStatsLocked(true);
writeHistoryLocked(true);
}
+ @GuardedBy("this")
void writeStatsLocked(boolean sync) {
if (mStatsFile == null) {
Slog.w(TAG,
@@ -15621,6 +15941,7 @@
}
@UnsupportedAppUsage
+ @GuardedBy("this")
public void readLocked() {
if (mDailyFile != null) {
readDailyStatsLocked();
@@ -15705,6 +16026,7 @@
return 0;
}
+ @GuardedBy("this")
void readHistoryBuffer(Parcel in) throws ParcelFormatException {
final int version = in.readInt();
if (version != VERSION) {
@@ -15784,6 +16106,7 @@
out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
}
+ @GuardedBy("this")
public void readSummaryFromParcel(Parcel in) throws ParcelFormatException {
final int version = in.readInt();
if (version != VERSION) {
@@ -15870,11 +16193,14 @@
mNextMaxDailyDeadlineMs = in.readLong();
mBatteryTimeToFullSeconds = in.readLong();
+ mMeasuredEnergyStatsConfig = MeasuredEnergyStats.Config.createFromParcel(in);
+
/**
* WARNING: Supported buckets may have changed across boots. Bucket mismatch is handled
* later when {@link #initMeasuredEnergyStatsLocked} is called.
*/
- mGlobalMeasuredEnergyStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(in);
+ mGlobalMeasuredEnergyStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(
+ mMeasuredEnergyStatsConfig, in);
mStartCount++;
@@ -16191,8 +16517,8 @@
u.mWifiRadioApWakeupCount = null;
}
- u.mUidMeasuredEnergyStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(in,
- /* template */ mGlobalMeasuredEnergyStats);
+ u.mUidMeasuredEnergyStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(
+ mMeasuredEnergyStatsConfig, in);
int NW = in.readInt();
if (NW > (MAX_WAKELOCKS_PER_UID+1)) {
@@ -16304,6 +16630,7 @@
*
* @param out the Parcel to be written to.
*/
+ @GuardedBy("this")
public void writeSummaryToParcel(Parcel out, boolean inclHistory) {
pullPendingStateUpdatesLocked();
@@ -16377,7 +16704,8 @@
out.writeLong(mNextMaxDailyDeadlineMs);
out.writeLong(mBatteryTimeToFullSeconds);
- MeasuredEnergyStats.writeSummaryToParcel(mGlobalMeasuredEnergyStats, out, false, false);
+ MeasuredEnergyStats.Config.writeToParcel(mMeasuredEnergyStatsConfig, out);
+ MeasuredEnergyStats.writeSummaryToParcel(mGlobalMeasuredEnergyStats, out);
mScreenOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mScreenDozeTimer.writeSummaryFromParcelLocked(out, nowRealtime);
@@ -16700,7 +17028,7 @@
out.writeInt(0);
}
- MeasuredEnergyStats.writeSummaryToParcel(u.mUidMeasuredEnergyStats, out, true, true);
+ MeasuredEnergyStats.writeSummaryToParcel(u.mUidMeasuredEnergyStats, out);
final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap();
int NW = wakeStats.size();
@@ -16822,10 +17150,13 @@
LongSamplingCounterArray.writeSummaryToParcelLocked(out, mBinderThreadCpuTimesUs);
}
+ @GuardedBy("this")
public void readFromParcel(Parcel in) {
readFromParcelLocked(in);
}
+ @GuardedBy("this")
+ @SuppressWarnings("GuardedBy") // errorprone false positive on u.readFromParcelLocked
void readFromParcelLocked(Parcel in) {
int magic = in.readInt();
if (magic != MAGIC) {
@@ -16966,9 +17297,9 @@
mLastWriteTimeMs = in.readLong();
mBatteryTimeToFullSeconds = in.readLong();
- if (in.readInt() != 0) {
- mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(in);
- }
+ mMeasuredEnergyStatsConfig = MeasuredEnergyStats.Config.createFromParcel(in);
+ mGlobalMeasuredEnergyStats =
+ MeasuredEnergyStats.createFromParcel(mMeasuredEnergyStatsConfig, in);
mRpmStats.clear();
int NRPMS = in.readInt();
@@ -17039,22 +17370,26 @@
for (int i = 0; i < numUids; i++) {
int uid = in.readInt();
Uid u = new Uid(this, uid, elapsedRealtimeMs, uptimeMs);
- u.readFromParcelLocked(mOnBatteryTimeBase, mOnBatteryScreenOffTimeBase, in);
+ u.readFromParcelLocked(mOnBatteryTimeBase, mOnBatteryScreenOffTimeBase,
+ in);
mUidStats.append(uid, u);
}
mBinderThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase);
}
+ @GuardedBy("this")
public void writeToParcel(Parcel out, int flags) {
writeToParcelLocked(out, true, flags);
}
+ @GuardedBy("this")
public void writeToParcelWithoutUids(Parcel out, int flags) {
writeToParcelLocked(out, false, flags);
}
@SuppressWarnings("unused")
+ @GuardedBy("this")
void writeToParcelLocked(Parcel out, boolean inclUids, int flags) {
// Need to update with current kernel wake lock counts.
pullPendingStateUpdatesLocked();
@@ -17170,6 +17505,8 @@
out.writeLong(mLastWriteTimeMs);
out.writeLong(mBatteryTimeToFullSeconds);
+ MeasuredEnergyStats.Config.writeToParcel(mMeasuredEnergyStatsConfig, out);
+
if (mGlobalMeasuredEnergyStats != null) {
out.writeInt(1);
mGlobalMeasuredEnergyStats.writeToParcel(out);
@@ -17332,6 +17669,7 @@
}
};
+ @GuardedBy("this")
public void prepareForDumpLocked() {
// Need to retrieve current kernel wake lock stats before printing.
pullPendingStateUpdatesLocked();
@@ -17343,6 +17681,7 @@
updateSystemServiceCallStats();
}
+ @GuardedBy("this")
public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
if (DEBUG) {
pw.println("mOnBatteryTimeBase:");
diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java
index d347f2e..8b0411d 100644
--- a/core/java/com/android/internal/os/ClassLoaderFactory.java
+++ b/core/java/com/android/internal/os/ClassLoaderFactory.java
@@ -80,15 +80,20 @@
*/
public static ClassLoader createClassLoader(String dexPath,
String librarySearchPath, ClassLoader parent, String classloaderName,
- List<ClassLoader> sharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<ClassLoader> sharedLibrariesLoadedAfter) {
ClassLoader[] arrayOfSharedLibraries = (sharedLibraries == null)
? null
: sharedLibraries.toArray(new ClassLoader[sharedLibraries.size()]);
+ ClassLoader[] arrayOfSharedLibrariesLoadedAfterApp = (sharedLibrariesLoadedAfter == null)
+ ? null
+ : sharedLibrariesLoadedAfter.toArray(
+ new ClassLoader[sharedLibrariesLoadedAfter.size()]);
if (isPathClassLoaderName(classloaderName)) {
- return new PathClassLoader(dexPath, librarySearchPath, parent, arrayOfSharedLibraries);
+ return new PathClassLoader(dexPath, librarySearchPath, parent, arrayOfSharedLibraries,
+ arrayOfSharedLibrariesLoadedAfterApp);
} else if (isDelegateLastClassLoaderName(classloaderName)) {
return new DelegateLastClassLoader(dexPath, librarySearchPath, parent,
- arrayOfSharedLibraries);
+ arrayOfSharedLibraries, arrayOfSharedLibrariesLoadedAfterApp);
}
throw new AssertionError("Invalid classLoaderName: " + classloaderName);
@@ -102,20 +107,20 @@
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
int targetSdkVersion, boolean isNamespaceShared, String classLoaderName) {
return createClassLoader(dexPath, librarySearchPath, libraryPermittedPath,
- parent, targetSdkVersion, isNamespaceShared, classLoaderName, null, null);
+ parent, targetSdkVersion, isNamespaceShared, classLoaderName, null, null, null);
}
-
/**
* Create a ClassLoader and initialize a linker-namespace for it.
*/
public static ClassLoader createClassLoader(String dexPath,
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
int targetSdkVersion, boolean isNamespaceShared, String classLoaderName,
- List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries,
+ List<ClassLoader> sharedLibrariesAfter) {
final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
- classLoaderName, sharedLibraries);
+ classLoaderName, sharedLibraries, sharedLibrariesAfter);
String sonameList = "";
if (nativeSharedLibraries != null) {
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index 72b57ab..0723766 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -29,6 +29,7 @@
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.LongMultiStateCounter;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -75,6 +76,147 @@
public @interface StandardPowerBucket {
}
+ private static final int INVALID_STATE = -1;
+
+ /**
+ * Configuration of measured energy stats: which power rails (buckets) are supported on
+ * this device, what custom power drains are supported etc.
+ */
+ public static class Config {
+ private final boolean[] mSupportedStandardBuckets;
+ @NonNull
+ private final String[] mCustomBucketNames;
+ private final boolean[] mSupportedMultiStateBuckets;
+ @NonNull
+ private final String[] mStateNames;
+
+ public Config(@NonNull boolean[] supportedStandardBuckets,
+ @Nullable String[] customBucketNames,
+ @NonNull int[] supportedMultiStateBuckets,
+ @Nullable String[] stateNames) {
+ mSupportedStandardBuckets = supportedStandardBuckets;
+ mCustomBucketNames = customBucketNames != null ? customBucketNames : new String[0];
+ mSupportedMultiStateBuckets =
+ new boolean[supportedStandardBuckets.length + mCustomBucketNames.length];
+ for (int bucket : supportedMultiStateBuckets) {
+ if (mSupportedStandardBuckets[bucket]) {
+ mSupportedMultiStateBuckets[bucket] = true;
+ }
+ }
+ mStateNames = stateNames != null ? stateNames : new String[] {""};
+ }
+
+ /**
+ * Returns true if the supplied Config is compatible with this one and therefore
+ * data collected with one of them will work with the other.
+ */
+ public boolean isCompatible(Config other) {
+ return Arrays.equals(mSupportedStandardBuckets, other.mSupportedStandardBuckets)
+ && Arrays.equals(mCustomBucketNames, other.mCustomBucketNames)
+ && Arrays.equals(mSupportedMultiStateBuckets,
+ other.mSupportedMultiStateBuckets)
+ && Arrays.equals(mStateNames, other.mStateNames);
+ }
+
+ /**
+ * Writes the Config object into the supplied Parcel.
+ */
+ public static void writeToParcel(@Nullable Config config, Parcel out) {
+ if (config == null) {
+ out.writeBoolean(false);
+ return;
+ }
+
+ out.writeBoolean(true);
+ out.writeInt(config.mSupportedStandardBuckets.length);
+ out.writeBooleanArray(config.mSupportedStandardBuckets);
+ out.writeStringArray(config.mCustomBucketNames);
+ int multiStateBucketCount = 0;
+ for (boolean supported : config.mSupportedMultiStateBuckets) {
+ if (supported) {
+ multiStateBucketCount++;
+ }
+ }
+ final int[] supportedMultiStateBuckets = new int[multiStateBucketCount];
+ int index = 0;
+ for (int bucket = 0; bucket < config.mSupportedMultiStateBuckets.length; bucket++) {
+ if (config.mSupportedMultiStateBuckets[bucket]) {
+ supportedMultiStateBuckets[index++] = bucket;
+ }
+ }
+ out.writeInt(multiStateBucketCount);
+ out.writeIntArray(supportedMultiStateBuckets);
+ out.writeStringArray(config.mStateNames);
+ }
+
+ /**
+ * Reads a Config object from the supplied Parcel.
+ */
+ @Nullable
+ public static Config createFromParcel(Parcel in) {
+ if (!in.readBoolean()) {
+ return null;
+ }
+
+ final int supportedStandardBucketCount = in.readInt();
+ final boolean[] supportedStandardBuckets = new boolean[supportedStandardBucketCount];
+ in.readBooleanArray(supportedStandardBuckets);
+ final String[] customBucketNames = in.readStringArray();
+ final int supportedMultiStateBucketCount = in.readInt();
+ final int[] supportedMultiStateBuckets = new int[supportedMultiStateBucketCount];
+ in.readIntArray(supportedMultiStateBuckets);
+ final String[] stateNames = in.readStringArray();
+ return new Config(supportedStandardBuckets, customBucketNames,
+ supportedMultiStateBuckets, stateNames);
+ }
+
+ /** Get number of possible buckets, including both standard and custom ones. */
+ private int getNumberOfBuckets() {
+ return mSupportedStandardBuckets.length + mCustomBucketNames.length;
+ }
+
+ /**
+ * Returns true if the specified charge bucket is tracked.
+ */
+ public boolean isSupportedBucket(int index) {
+ return mSupportedStandardBuckets[index];
+ }
+
+ @NonNull
+ public String[] getCustomBucketNames() {
+ return mCustomBucketNames;
+ }
+
+ /**
+ * Returns true if the specified charge bucket is tracked on a per-state basis.
+ */
+ public boolean isSupportedMultiStateBucket(int index) {
+ return mSupportedMultiStateBuckets[index];
+ }
+
+ public String[] getStateNames() {
+ return mStateNames;
+ }
+
+ /**
+ * If the index is a standard bucket, returns its name; otherwise returns its prefixed
+ * custom bucket number.
+ */
+ private String getBucketName(int index) {
+ if (isValidStandardBucket(index)) {
+ return DebugUtils.valueToString(MeasuredEnergyStats.class, "POWER_BUCKET_", index);
+ }
+ final int customBucket = indexToCustomBucket(index);
+ StringBuilder name = new StringBuilder().append("CUSTOM_").append(customBucket);
+ if (!TextUtils.isEmpty(mCustomBucketNames[customBucket])) {
+ name.append('(').append(mCustomBucketNames[customBucket]).append(')');
+ }
+ return name.toString();
+ }
+ }
+
+ private final Config mConfig;
+
/**
* Total charge (in microcoulombs) that a power bucket (including both
* {@link StandardPowerBucket} and custom buckets) has accumulated since the last reset.
@@ -90,74 +232,76 @@
*/
private final long[] mAccumulatedChargeMicroCoulomb;
- private final String[] mCustomBucketNames;
+ private LongMultiStateCounter[] mAccumulatedMultiStateChargeMicroCoulomb;
+ private int mState = INVALID_STATE;
+ private long mStateChangeTimestampMs;
/**
* Creates a MeasuredEnergyStats set to support the provided power buckets.
* supportedStandardBuckets must be of size {@link #NUMBER_STANDARD_POWER_BUCKETS}.
* numCustomBuckets >= 0 is the number of (non-standard) custom power buckets on the device.
*/
- public MeasuredEnergyStats(@NonNull boolean[] supportedStandardBuckets,
- @Nullable String[] customBucketNames) {
- mCustomBucketNames = customBucketNames == null ? new String[0] : customBucketNames;
- final int numTotalBuckets = NUMBER_STANDARD_POWER_BUCKETS + mCustomBucketNames.length;
+ public MeasuredEnergyStats(MeasuredEnergyStats.Config config) {
+ mConfig = config;
+ final int numTotalBuckets = config.getNumberOfBuckets();
mAccumulatedChargeMicroCoulomb = new long[numTotalBuckets];
// Initialize to all zeros where supported, otherwise POWER_DATA_UNAVAILABLE.
// All custom buckets are, by definition, supported, so their values stay at 0.
for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_POWER_BUCKETS; stdBucket++) {
- if (!supportedStandardBuckets[stdBucket]) {
+ if (!mConfig.mSupportedStandardBuckets[stdBucket]) {
mAccumulatedChargeMicroCoulomb[stdBucket] = POWER_DATA_UNAVAILABLE;
}
}
}
/**
- * Creates a new zero'd MeasuredEnergyStats, using the template to determine which buckets are
- * supported. This certainly does NOT produce an exact clone of the template.
+ * Reads a MeasuredEnergyStats from the supplied Parcel.
*/
- private MeasuredEnergyStats(MeasuredEnergyStats template) {
- final int numIndices = template.getNumberOfIndices();
- mAccumulatedChargeMicroCoulomb = new long[numIndices];
- // Initialize to all zeros where supported, otherwise POWER_DATA_UNAVAILABLE.
- // All custom buckets are, by definition, supported, so their values stay at 0.
- for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_POWER_BUCKETS; stdBucket++) {
- if (!template.isIndexSupported(stdBucket)) {
- mAccumulatedChargeMicroCoulomb[stdBucket] = POWER_DATA_UNAVAILABLE;
- }
+ @Nullable
+ public static MeasuredEnergyStats createFromParcel(Config config, Parcel in) {
+ if (!in.readBoolean()) {
+ return null;
}
- mCustomBucketNames = template.getCustomBucketNames();
- }
-
- /**
- * Creates a new zero'd MeasuredEnergyStats, using the template to determine which buckets are
- * supported.
- */
- public static MeasuredEnergyStats createFromTemplate(MeasuredEnergyStats template) {
- return new MeasuredEnergyStats(template);
- }
-
- /**
- * Constructor for creating a temp MeasuredEnergyStats.
- * See {@link #createAndReadSummaryFromParcel(Parcel, MeasuredEnergyStats)}.
- */
- private MeasuredEnergyStats(int numIndices) {
- mAccumulatedChargeMicroCoulomb = new long[numIndices];
- mCustomBucketNames = new String[numIndices - NUMBER_STANDARD_POWER_BUCKETS];
+ return new MeasuredEnergyStats(config, in);
}
/** Construct from parcel. */
- public MeasuredEnergyStats(Parcel in) {
+ public MeasuredEnergyStats(MeasuredEnergyStats.Config config, Parcel in) {
+ mConfig = config;
+
final int size = in.readInt();
mAccumulatedChargeMicroCoulomb = new long[size];
in.readLongArray(mAccumulatedChargeMicroCoulomb);
- mCustomBucketNames = in.readStringArray();
+ if (in.readBoolean()) {
+ mAccumulatedMultiStateChargeMicroCoulomb = new LongMultiStateCounter[size];
+ for (int i = 0; i < size; i++) {
+ if (in.readBoolean()) {
+ mAccumulatedMultiStateChargeMicroCoulomb[i] =
+ LongMultiStateCounter.CREATOR.createFromParcel(in);
+ }
+ }
+ } else {
+ mAccumulatedMultiStateChargeMicroCoulomb = null;
+ }
}
/** Write to parcel */
public void writeToParcel(Parcel out) {
out.writeInt(mAccumulatedChargeMicroCoulomb.length);
out.writeLongArray(mAccumulatedChargeMicroCoulomb);
- out.writeStringArray(mCustomBucketNames);
+ if (mAccumulatedMultiStateChargeMicroCoulomb != null) {
+ out.writeBoolean(true);
+ for (LongMultiStateCounter counter : mAccumulatedMultiStateChargeMicroCoulomb) {
+ if (counter != null) {
+ out.writeBoolean(true);
+ counter.writeToParcel(out, 0);
+ } else {
+ out.writeBoolean(false);
+ }
+ }
+ } else {
+ out.writeBoolean(false);
+ }
}
/**
@@ -167,17 +311,27 @@
* Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if summary
* parceling changes.
*
- * Corresponding write performed by {@link #writeSummaryToParcel(Parcel, boolean)}.
+ * Corresponding write performed by {@link #writeSummaryToParcel(Parcel)}.
*/
- private void readSummaryFromParcel(Parcel in, boolean overwriteAvailability) {
+ private void readSummaryFromParcel(Parcel in) {
final int numWrittenEntries = in.readInt();
for (int entry = 0; entry < numWrittenEntries; entry++) {
final int index = in.readInt();
final long chargeUC = in.readLong();
- if (overwriteAvailability) {
- mAccumulatedChargeMicroCoulomb[index] = chargeUC;
- } else {
+ LongMultiStateCounter multiStateCounter = null;
+ if (in.readBoolean()) {
+ multiStateCounter = LongMultiStateCounter.CREATOR.createFromParcel(in);
+ }
+
+ if (index < mAccumulatedChargeMicroCoulomb.length) {
setValueIfSupported(index, chargeUC);
+ if (multiStateCounter != null) {
+ if (mAccumulatedMultiStateChargeMicroCoulomb == null) {
+ mAccumulatedMultiStateChargeMicroCoulomb =
+ new LongMultiStateCounter[numWrittenEntries];
+ }
+ mAccumulatedMultiStateChargeMicroCoulomb[index] = multiStateCounter;
+ }
}
}
}
@@ -186,20 +340,26 @@
* Write to summary parcel.
* Note: Measured subsystem availability may be different when the summary parcel is read.
*
- * Corresponding read performed by {@link #readSummaryFromParcel(Parcel, boolean)}.
+ * Corresponding read performed by {@link #readSummaryFromParcel(Parcel)}.
*/
- private void writeSummaryToParcel(Parcel out, boolean skipZero) {
+ private void writeSummaryToParcel(Parcel out) {
final int posOfNumWrittenEntries = out.dataPosition();
out.writeInt(0);
int numWrittenEntries = 0;
// Write only the supported buckets (with non-zero charge, if applicable).
for (int index = 0; index < mAccumulatedChargeMicroCoulomb.length; index++) {
final long charge = mAccumulatedChargeMicroCoulomb[index];
- if (charge < 0) continue;
- if (charge == 0 && skipZero) continue;
+ if (charge <= 0) continue;
out.writeInt(index);
out.writeLong(charge);
+ if (mAccumulatedMultiStateChargeMicroCoulomb != null
+ && mAccumulatedMultiStateChargeMicroCoulomb[index] != null) {
+ out.writeBoolean(true);
+ mAccumulatedMultiStateChargeMicroCoulomb[index].writeToParcel(out, 0);
+ } else {
+ out.writeBoolean(false);
+ }
numWrittenEntries++;
}
final int currPos = out.dataPosition();
@@ -208,40 +368,87 @@
out.setDataPosition(currPos);
}
- /** Get number of possible buckets, including both standard and custom ones. */
- private int getNumberOfIndices() {
- return mAccumulatedChargeMicroCoulomb.length;
- }
-
-
/** Updates the given standard power bucket with the given charge if accumulate is true. */
public void updateStandardBucket(@StandardPowerBucket int bucket, long chargeDeltaUC) {
+ updateStandardBucket(bucket, chargeDeltaUC, 0);
+ }
+
+ /**
+ * Updates the given standard power bucket with the given charge if supported.
+ * @param timestampMs elapsed realtime in milliseconds
+ */
+ public void updateStandardBucket(@StandardPowerBucket int bucket, long chargeDeltaUC,
+ long timestampMs) {
checkValidStandardBucket(bucket);
- updateEntry(bucket, chargeDeltaUC);
+ updateEntry(bucket, chargeDeltaUC, timestampMs);
}
/** Updates the given custom power bucket with the given charge if accumulate is true. */
public void updateCustomBucket(int customBucket, long chargeDeltaUC) {
+ updateCustomBucket(customBucket, chargeDeltaUC, 0);
+ }
+
+ /**
+ * Updates the given custom power bucket with the given charge if supported.
+ * @param timestampMs elapsed realtime in milliseconds
+ */
+ public void updateCustomBucket(int customBucket, long chargeDeltaUC, long timestampMs) {
if (!isValidCustomBucket(customBucket)) {
Slog.e(TAG, "Attempted to update invalid custom bucket " + customBucket);
return;
}
final int index = customBucketToIndex(customBucket);
- updateEntry(index, chargeDeltaUC);
+ updateEntry(index, chargeDeltaUC, timestampMs);
}
- /** Updates the given index with the given charge if accumulate is true. */
- private void updateEntry(int index, long chargeDeltaUC) {
+ /** Updates the given bucket with the given charge delta. */
+ private void updateEntry(int index, long chargeDeltaUC, long timestampMs) {
if (mAccumulatedChargeMicroCoulomb[index] >= 0L) {
mAccumulatedChargeMicroCoulomb[index] += chargeDeltaUC;
+ if (mState != INVALID_STATE && mConfig.isSupportedMultiStateBucket(index)) {
+ if (mAccumulatedMultiStateChargeMicroCoulomb == null) {
+ mAccumulatedMultiStateChargeMicroCoulomb =
+ new LongMultiStateCounter[mAccumulatedChargeMicroCoulomb.length];
+ }
+ LongMultiStateCounter counter =
+ mAccumulatedMultiStateChargeMicroCoulomb[index];
+ if (counter == null) {
+ counter = new LongMultiStateCounter(mConfig.mStateNames.length);
+ mAccumulatedMultiStateChargeMicroCoulomb[index] = counter;
+ counter.setState(mState, mStateChangeTimestampMs);
+ counter.updateValue(0, mStateChangeTimestampMs);
+ }
+ counter.updateValue(mAccumulatedChargeMicroCoulomb[index], timestampMs);
+ }
} else {
Slog.wtf(TAG, "Attempting to add " + chargeDeltaUC + " to unavailable bucket "
- + getBucketName(index) + " whose value was "
+ + mConfig.getBucketName(index) + " whose value was "
+ mAccumulatedChargeMicroCoulomb[index]);
}
}
/**
+ * Updates the "state" on all multi-state counters used by this MeasuredEnergyStats. Further
+ * accumulated charge updates will assign the deltas to this state, until the state changes.
+ *
+ * If setState is never called on a MeasuredEnergyStats object, then it does not track
+ * per-state usage.
+ */
+ public void setState(int state, long timestampMs) {
+ mState = state;
+ mStateChangeTimestampMs = timestampMs;
+ if (mAccumulatedMultiStateChargeMicroCoulomb == null) {
+ return;
+ }
+ for (int i = 0; i < mAccumulatedMultiStateChargeMicroCoulomb.length; i++) {
+ LongMultiStateCounter counter = mAccumulatedMultiStateChargeMicroCoulomb[i];
+ if (counter != null) {
+ counter.setState(state, timestampMs);
+ }
+ }
+ }
+
+ /**
* Return accumulated charge (in microcouloumb) for a standard power bucket since last reset.
* Returns {@link android.os.BatteryStats#POWER_DATA_UNAVAILABLE} if this data is unavailable.
* @throws IllegalArgumentException if no such {@link StandardPowerBucket}.
@@ -252,6 +459,26 @@
}
/**
+ * Returns the accumulated charge (in microcouloumb) for the standard power bucket and
+ * the specified state since last reset.
+ *
+ * Returns {@link android.os.BatteryStats#POWER_DATA_UNAVAILABLE} if this data is unavailable.
+ */
+ public long getAccumulatedStandardBucketCharge(@StandardPowerBucket int bucket, int state) {
+ if (!mConfig.isSupportedMultiStateBucket(bucket)) {
+ return POWER_DATA_UNAVAILABLE;
+ }
+ if (mAccumulatedMultiStateChargeMicroCoulomb == null) {
+ return 0;
+ }
+ final LongMultiStateCounter counter = mAccumulatedMultiStateChargeMicroCoulomb[bucket];
+ if (counter == null) {
+ return 0;
+ }
+ return counter.getCount(state);
+ }
+
+ /**
* Return accumulated charge (in microcoulomb) for the a custom power bucket since last
* reset.
* Returns {@link android.os.BatteryStats#POWER_DATA_UNAVAILABLE} if this data is unavailable.
@@ -289,32 +516,6 @@
}
/**
- * Create a MeasuredEnergyStats object from a summary parcel.
- *
- * Corresponding write performed by
- * {@link #writeSummaryToParcel(MeasuredEnergyStats, Parcel, boolean, boolean)}.
- *
- * @return a new MeasuredEnergyStats object as described.
- * Returns null if the parcel indicates there is no data to populate.
- */
- public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in) {
- final int arraySize = in.readInt();
- // Check if any MeasuredEnergyStats exists on the parcel
- if (arraySize == 0) return null;
-
- final String[] customBucketNames;
- if (in.readBoolean()) {
- customBucketNames = in.readStringArray();
- } else {
- customBucketNames = new String[0];
- }
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(
- new boolean[NUMBER_STANDARD_POWER_BUCKETS], customBucketNames);
- stats.readSummaryFromParcel(in, true);
- return stats;
- }
-
- /**
* Create a MeasuredEnergyStats using the template to determine which buckets are supported,
* and populate this new object from the given parcel.
*
@@ -322,44 +523,37 @@
* possible (not necessarily supported) standard and custom buckets.
*
* Corresponding write performed by
- * {@link #writeSummaryToParcel(MeasuredEnergyStats, Parcel, boolean, boolean)}.
+ * {@link #writeSummaryToParcel(MeasuredEnergyStats, Parcel)}.
*
* @return a new MeasuredEnergyStats object as described.
* Returns null if the stats contain no non-0 information (such as if template is null
* or if the parcel indicates there is no data to populate).
- *
- * @see #createFromTemplate
*/
- public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in,
- @Nullable MeasuredEnergyStats template) {
+ public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(
+ @Nullable Config config, Parcel in) {
final int arraySize = in.readInt();
// Check if any MeasuredEnergyStats exists on the parcel
if (arraySize == 0) return null;
- boolean includesCustomBucketNames = in.readBoolean();
- if (includesCustomBucketNames) {
- // Consume the array of custom bucket names. They are already included in the
- // template.
- in.readStringArray();
- }
- if (template == null) {
+ if (config == null) {
// Nothing supported anymore. Create placeholder object just to consume the parcel data.
- final MeasuredEnergyStats mes = new MeasuredEnergyStats(arraySize);
- mes.readSummaryFromParcel(in, false);
+ final MeasuredEnergyStats mes = new MeasuredEnergyStats(
+ new Config(new boolean[arraySize], null, new int[0], new String[]{""}));
+ mes.readSummaryFromParcel(in);
return null;
}
- if (arraySize != template.getNumberOfIndices()) {
+ if (arraySize != config.getNumberOfBuckets()) {
Slog.wtf(TAG, "Size of MeasuredEnergyStats parcel (" + arraySize
- + ") does not match template (" + template.getNumberOfIndices() + ").");
+ + ") does not match config (" + config.getNumberOfBuckets() + ").");
// Something is horribly wrong. Just consume the parcel and return null.
- final MeasuredEnergyStats mes = new MeasuredEnergyStats(arraySize);
- mes.readSummaryFromParcel(in, false);
+ final MeasuredEnergyStats mes = new MeasuredEnergyStats(config);
+ mes.readSummaryFromParcel(in);
return null;
}
- final MeasuredEnergyStats stats = createFromTemplate(template);
- stats.readSummaryFromParcel(in, false);
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+ stats.readSummaryFromParcel(in);
if (stats.containsInterestingData()) {
return stats;
} else {
@@ -379,28 +573,20 @@
/**
* Write a MeasuredEnergyStats to a parcel. If the stats is null, just write a 0.
*
- * Corresponding read performed by {@link #createAndReadSummaryFromParcel(Parcel)}
- * and {@link #createAndReadSummaryFromParcel(Parcel, MeasuredEnergyStats)}.
+ * Corresponding read performed by {@link #createAndReadSummaryFromParcel}.
*/
- public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats,
- Parcel dest, boolean skipZero, boolean skipCustomBucketNames) {
+ public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats, Parcel dest) {
if (stats == null) {
dest.writeInt(0);
return;
}
- dest.writeInt(stats.getNumberOfIndices());
- if (!skipCustomBucketNames) {
- dest.writeBoolean(true);
- dest.writeStringArray(stats.getCustomBucketNames());
- } else {
- dest.writeBoolean(false);
- }
- stats.writeSummaryToParcel(dest, skipZero);
+ dest.writeInt(stats.mConfig.getNumberOfBuckets());
+ stats.writeSummaryToParcel(dest);
}
/** Reset accumulated charges. */
private void reset() {
- final int numIndices = getNumberOfIndices();
+ final int numIndices = mConfig.getNumberOfBuckets();
for (int index = 0; index < numIndices; index++) {
setValueIfSupported(index, 0L);
}
@@ -431,46 +617,32 @@
return mAccumulatedChargeMicroCoulomb[index] != POWER_DATA_UNAVAILABLE;
}
- /** Check if the supported power buckets are precisely those given. */
- public boolean isSupportEqualTo(
- @NonNull boolean[] queriedStandardBuckets, @Nullable String[] customBucketNames) {
- if (customBucketNames == null) {
- //In practice customBucketNames should never be null, but sanitize it just to be sure.
- customBucketNames = new String[0];
- }
-
- final int numBuckets = getNumberOfIndices();
- final int numCustomBuckets = customBucketNames == null ? 0 : customBucketNames.length;
- if (numBuckets != NUMBER_STANDARD_POWER_BUCKETS + numCustomBuckets) {
- return false;
- }
-
- if (!Arrays.equals(mCustomBucketNames, customBucketNames)) {
- return false;
- }
-
- for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_POWER_BUCKETS; stdBucket++) {
- if (isStandardBucketSupported(stdBucket) != queriedStandardBuckets[stdBucket]) {
- return false;
- }
- }
- return true;
- }
-
- public String[] getCustomBucketNames() {
- return mCustomBucketNames;
- }
-
/** Dump debug data. */
public void dump(PrintWriter pw) {
pw.print(" ");
for (int index = 0; index < mAccumulatedChargeMicroCoulomb.length; index++) {
- pw.print(getBucketName(index));
+ pw.print(mConfig.getBucketName(index));
pw.print(" : ");
pw.print(mAccumulatedChargeMicroCoulomb[index]);
if (!isIndexSupported(index)) {
pw.print(" (unsupported)");
}
+ if (mAccumulatedMultiStateChargeMicroCoulomb != null) {
+ final LongMultiStateCounter counter =
+ mAccumulatedMultiStateChargeMicroCoulomb[index];
+ if (counter != null) {
+ pw.print(" [");
+ for (int i = 0; i < mConfig.mStateNames.length; i++) {
+ if (i != 0) {
+ pw.print(" ");
+ }
+ pw.print(mConfig.mStateNames[i]);
+ pw.print(": ");
+ pw.print(counter.getCount(i));
+ }
+ pw.print("]");
+ }
+ }
if (index != mAccumulatedChargeMicroCoulomb.length - 1) {
pw.print(", ");
}
@@ -478,22 +650,6 @@
pw.println();
}
- /**
- * If the index is a standard bucket, returns its name; otherwise returns its prefixed custom
- * bucket number.
- */
- private String getBucketName(int index) {
- if (isValidStandardBucket(index)) {
- return DebugUtils.valueToString(MeasuredEnergyStats.class, "POWER_BUCKET_", index);
- }
- final int customBucket = indexToCustomBucket(index);
- StringBuilder name = new StringBuilder().append("CUSTOM_").append(customBucket);
- if (mCustomBucketNames != null && !TextUtils.isEmpty(mCustomBucketNames[customBucket])) {
- name.append('(').append(mCustomBucketNames[customBucket]).append(')');
- }
- return name.toString();
- }
-
/** Get the number of custom power buckets on this device. */
public int getNumberCustomPowerBuckets() {
return mAccumulatedChargeMicroCoulomb.length - NUMBER_STANDARD_POWER_BUCKETS;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 0f245e6..4e758e6 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -290,4 +290,5 @@
void runGcForTest();
void requestAddTile(in ComponentName componentName, in CharSequence appName, in CharSequence label, in Icon icon, in IAddTileResultCallback callback);
+ void cancelRequestAddTile(in String packageName);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 49c32be..40a3951 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -161,4 +161,5 @@
void suppressAmbientDisplay(boolean suppress);
void requestAddTile(in ComponentName componentName, in CharSequence label, in Icon icon, int userId, in IAddTileResultCallback callback);
+ void cancelRequestAddTile(in String packageName);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 1a1a8ba..2bd385c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -404,6 +404,7 @@
name: "libandroid_runtime_vm_headers",
host_supported: true,
vendor_available: true,
+ recovery_available: true,
// TODO(b/153609531): remove when libbinder is not native_bridge_supported
native_bridge_supported: true,
// Allow only modules from the following list to create threads that can be
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 793b4eb..61b91dd 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -959,8 +959,7 @@
return IPCThreadState::self()->getCallingUid();
}
-static jboolean android_os_Binder_isHandlingTransaction()
-{
+static jboolean android_os_Binder_isDirectlyHandlingTransaction() {
return getCurrentServingCall() == BinderCallType::BINDER;
}
@@ -1056,6 +1055,7 @@
// ----------------------------------------------------------------------------
+// clang-format off
static const JNINativeMethod gBinderMethods[] = {
/* name, signature, funcPtr */
// @CriticalNative
@@ -1063,7 +1063,7 @@
// @CriticalNative
{ "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
// @CriticalNative
- { "isHandlingTransaction", "()Z", (void*)android_os_Binder_isHandlingTransaction },
+ { "isDirectlyHandlingTransaction", "()Z", (void*)android_os_Binder_isDirectlyHandlingTransaction },
// @CriticalNative
{ "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
// @CriticalNative
@@ -1088,6 +1088,7 @@
{ "getExtension", "()Landroid/os/IBinder;", (void*)android_os_Binder_getExtension },
{ "setExtension", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension },
};
+// clang-format on
const char* const kBinderPathName = "android/os/Binder";
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 6f5cc53..40f6e4f 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -138,6 +138,14 @@
return true;
}
+ // Allow Runtime Resource Overlays inside APEXes.
+ static const char* kOverlayPathSuffix = "/overlay";
+ if (android::base::StartsWith(path, kApexPrefix) &&
+ android::base::EndsWith(android::base::Dirname(path), kOverlayPathSuffix) &&
+ android::base::EndsWith(path, kApkSuffix) && path.find("/../") == std::string::npos) {
+ return true;
+ }
+
static const char* kOverlayIdmapPrefix = "/data/resource-cache/";
static const char* kOverlayIdmapSuffix = ".apk@idmap";
if (android::base::StartsWith(path, kOverlayIdmapPrefix) &&
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 40e3f60..3248cf5 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -202,7 +202,6 @@
optional .com.android.server.wm.IdentifierProto resumed_activity = 24;
repeated TaskProto tasks = 25 [deprecated=true];
optional bool display_ready = 26;
-
optional WindowStateProto input_method_target = 27;
optional WindowStateProto input_method_input_target = 28;
optional WindowStateProto input_method_control_target = 29;
@@ -214,6 +213,9 @@
optional int32 ime_policy = 34;
repeated InsetsSourceProviderProto insets_source_providers = 35;
+ optional bool is_sleeping = 36;
+ repeated string sleep_tokens = 37;
+
}
/* represents DisplayArea object */
@@ -527,7 +529,7 @@
message WindowFramesProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional .android.graphics.RectProto containing_frame = 1;
+ optional .android.graphics.RectProto containing_frame = 1 [deprecated=true];
optional .android.graphics.RectProto content_frame = 2 [deprecated=true];
optional .android.graphics.RectProto decor_frame = 3 [deprecated=true];
optional .android.graphics.RectProto display_frame = 4;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 329f02a..b3a16d9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5911,6 +5911,11 @@
<permission android:name="android.permission.READ_GLOBAL_APP_SEARCH_DATA"
android:protectionLevel="internal|role" />
+ <!-- @SystemApi Allows an application to create virtual devices in VirtualDeviceManager.
+ @hide -->
+ <permission android:name="android.permission.CREATE_VIRTUAL_DEVICE"
+ android:protectionLevel="internal|role" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 765495a..06333e1 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1984,6 +1984,22 @@
<enum name="KEYCODE_THUMBS_UP" value="286" />
<enum name="KEYCODE_THUMBS_DOWN" value="287" />
<enum name="KEYCODE_PROFILE_SWITCH" value="288" />
+ <enum name="KEYCODE_VIDEO_APP_1" value="289" />
+ <enum name="KEYCODE_VIDEO_APP_2" value="290" />
+ <enum name="KEYCODE_VIDEO_APP_3" value="291" />
+ <enum name="KEYCODE_VIDEO_APP_4" value="292" />
+ <enum name="KEYCODE_VIDEO_APP_5" value="293" />
+ <enum name="KEYCODE_VIDEO_APP_6" value="294" />
+ <enum name="KEYCODE_VIDEO_APP_7" value="295" />
+ <enum name="KEYCODE_VIDEO_APP_8" value="296" />
+ <enum name="KEYCODE_FEATURED_APP_1" value="297" />
+ <enum name="KEYCODE_FEATURED_APP_2" value="298" />
+ <enum name="KEYCODE_FEATURED_APP_3" value="299" />
+ <enum name="KEYCODE_FEATURED_APP_4" value="300" />
+ <enum name="KEYCODE_DEMO_APP_1" value="301" />
+ <enum name="KEYCODE_DEMO_APP_2" value="302" />
+ <enum name="KEYCODE_DEMO_APP_3" value="303" />
+ <enum name="KEYCODE_DEMO_APP_4" value="304" />
</attr>
<!-- ***************************************************************** -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 74ce8f2..e4b3991 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5280,6 +5280,11 @@
to 0, the seconds hand will be disabled. -->
<integer name="config_defaultAnalogClockSecondsHandFps">1</integer>
+ <!-- List of shared library packages that should be loaded by the classloader after the
+ code and resources provided by applications. This value will be set by the manufacturer -->
+ <string-array name="config_sharedLibrariesLoadedAfterApp" translatable="false">
+ </string-array>
+
<!-- the number of the max cached processes in the system. -->
<integer name="config_customizedMaxCachedProcesses">32</integer>
diff --git a/core/res/res/values/dimens_car.xml b/core/res/res/values/dimens_car.xml
index 36f1edb..c5dddb8 100644
--- a/core/res/res/values/dimens_car.xml
+++ b/core/res/res/values/dimens_car.xml
@@ -17,7 +17,7 @@
-->
<resources>
<dimen name="car_large_avatar_size">96dp</dimen>
- <dimen name="car_large_avatar_badge_size">32dp</dimen>
+ <dimen name="car_large_avatar_badge_size">24dp</dimen>
<!-- Application Bar -->
<dimen name="car_app_bar_height">80dp</dimen>
<!-- Margin -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 846ec60..f166b73 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4559,6 +4559,10 @@
<java-symbol type="bool" name="config_volumeShowRemoteSessions" />
+ <!-- List of shared library packages that should be loaded by the classloader after the
+ code and resources provided by applications. -->
+ <java-symbol type="array" name="config_sharedLibrariesLoadedAfterApp" />
+
<java-symbol type="integer" name="config_customizedMaxCachedProcesses" />
<java-symbol type="color" name="overview_background"/>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index a65de91..205c517 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -105,6 +105,9 @@
http://www.tja.ee/public/documents/Elektrooniline_side/Oigusaktid/ENG/Estonian_Numbering_Plan_annex_06_09_2010.mht -->
<shortcode country="ee" pattern="1\\d{2,4}" premium="90\\d{5}|15330|1701[0-3]" free="116\\d{3}|95034" />
+ <!-- Egypt: 4 digits, known codes listed -->
+ <shortcode country="eg" pattern="\\d{4}" free="1499" />
+
<!-- Spain: 5-6 digits: 25xxx, 27xxx, 280xx, 35xxx, 37xxx, 795xxx, 797xxx, 995xxx, 997xxx, plus EU.
http://www.legallink.es/?q=en/content/which-current-regulatory-status-premium-rate-services-spain -->
<shortcode country="es" premium="[23][57]\\d{3}|280\\d{2}|[79]9[57]\\d{3}" free="116\\d{3}|22791|222145|22189" />
@@ -196,7 +199,7 @@
<shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223|1662" />
<!-- Nigeria -->
- <shortcode country="ng" pattern="\\d{1,5}" free="2441|55019" />
+ <shortcode country="ng" pattern="\\d{1,5}" free="2441|55020" />
<!-- Norway: 4-5 digits (not confirmed), known premium codes listed -->
<shortcode country="no" pattern="\\d{4,5}" premium="2201|222[67]" free="2171" />
diff --git a/core/tests/bluetoothtests/Android.bp b/core/tests/bluetoothtests/Android.bp
index a2e4dff..68416dd 100644
--- a/core/tests/bluetoothtests/Android.bp
+++ b/core/tests/bluetoothtests/Android.bp
@@ -15,7 +15,10 @@
"android.test.runner",
"android.test.base",
],
- static_libs: ["junit"],
+ static_libs: [
+ "junit",
+ "modules-utils-bytesmatcher",
+ ],
platform_apis: true,
certificate: "platform",
}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
index c287ea9..4e817d4 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
@@ -16,11 +16,11 @@
package android.bluetooth.le;
-import android.os.BytesMatcher;
import android.os.ParcelUuid;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.util.HexDump;
+import com.android.modules.utils.BytesMatcher;
import junit.framework.TestCase;
diff --git a/core/tests/coretests/src/android/net/SntpClientTest.java b/core/tests/coretests/src/android/net/SntpClientTest.java
index b400b9b..ba7df1e 100644
--- a/core/tests/coretests/src/android/net/SntpClientTest.java
+++ b/core/tests/coretests/src/android/net/SntpClientTest.java
@@ -26,6 +26,7 @@
import android.net.sntp.Duration64;
import android.net.sntp.Timestamp64;
+import android.platform.test.annotations.Presubmit;
import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
@@ -49,6 +50,7 @@
import java.util.Random;
import java.util.function.Supplier;
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class SntpClientTest {
private static final String TAG = "SntpClientTest";
diff --git a/core/tests/coretests/src/android/net/sntp/Duration64Test.java b/core/tests/coretests/src/android/net/sntp/Duration64Test.java
index 60b69f6..b228596 100644
--- a/core/tests/coretests/src/android/net/sntp/Duration64Test.java
+++ b/core/tests/coretests/src/android/net/sntp/Duration64Test.java
@@ -21,6 +21,8 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.Presubmit;
+
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
@@ -31,6 +33,7 @@
import java.time.LocalDateTime;
import java.time.ZoneOffset;
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class Duration64Test {
diff --git a/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java b/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
index 1b1c500..200c80e 100644
--- a/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
+++ b/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
@@ -21,6 +21,8 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.platform.test.annotations.Presubmit;
+
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
@@ -31,6 +33,7 @@
import java.util.Random;
import java.util.Set;
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class Timestamp64Test {
diff --git a/core/tests/coretests/src/android/os/BytesMatcherTest.java b/core/tests/coretests/src/android/os/BytesMatcherTest.java
deleted file mode 100644
index b28e309..0000000
--- a/core/tests/coretests/src/android/os/BytesMatcherTest.java
+++ /dev/null
@@ -1,178 +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.os;
-
-import static com.android.internal.util.HexDump.hexStringToByteArray;
-
-import android.bluetooth.BluetoothUuid;
-import android.net.MacAddress;
-
-import androidx.test.filters.SmallTest;
-
-import junit.framework.TestCase;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-@SmallTest
-public class BytesMatcherTest extends TestCase {
- @Test
- public void testEmpty() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("");
- assertFalse(matcher.test(hexStringToByteArray("cafe")));
- assertFalse(matcher.test(hexStringToByteArray("")));
- }
-
- @Test
- public void testExact() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+cafe");
- assertTrue(matcher.test(hexStringToByteArray("cafe")));
- assertFalse(matcher.test(hexStringToByteArray("beef")));
- assertFalse(matcher.test(hexStringToByteArray("ca")));
- assertFalse(matcher.test(hexStringToByteArray("cafe00")));
- }
-
- @Test
- public void testMask() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+cafe/ff00");
- assertTrue(matcher.test(hexStringToByteArray("cafe")));
- assertTrue(matcher.test(hexStringToByteArray("ca88")));
- assertFalse(matcher.test(hexStringToByteArray("beef")));
- assertFalse(matcher.test(hexStringToByteArray("ca")));
- assertFalse(matcher.test(hexStringToByteArray("cafe00")));
- }
-
- @Test
- public void testPrefix() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("⊆cafe,⊆beef/ff00");
- assertTrue(matcher.test(hexStringToByteArray("cafe")));
- assertFalse(matcher.test(hexStringToByteArray("caff")));
- assertTrue(matcher.test(hexStringToByteArray("cafecafe")));
- assertFalse(matcher.test(hexStringToByteArray("ca")));
- assertTrue(matcher.test(hexStringToByteArray("beef")));
- assertTrue(matcher.test(hexStringToByteArray("beff")));
- assertTrue(matcher.test(hexStringToByteArray("beffbeff")));
- assertFalse(matcher.test(hexStringToByteArray("be")));
- }
-
- @Test
- public void testMacAddress() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+cafe00112233/ffffff000000");
- assertTrue(matcher.testMacAddress(
- MacAddress.fromString("ca:fe:00:00:00:00")));
- assertFalse(matcher.testMacAddress(
- MacAddress.fromString("f0:0d:00:00:00:00")));
- }
-
- @Test
- public void testBluetoothUuid() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+cafe/ff00");
- assertTrue(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("cafe"))));
- assertFalse(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("beef"))));
- }
-
- /**
- * Verify that single matcher can be configured to match Bluetooth UUIDs of
- * varying lengths.
- */
- @Test
- public void testBluetoothUuid_Mixed() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+aaaa/ff00,+bbbbbbbb/ffff0000");
- assertTrue(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("aaaa"))));
- assertFalse(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("bbbb"))));
- assertTrue(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("bbbbbbbb"))));
- assertFalse(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("aaaaaaaa"))));
- }
-
- @Test
- public void testSerialize_Empty() throws Exception {
- BytesMatcher matcher = new BytesMatcher();
- matcher = BytesMatcher.decode(BytesMatcher.encode(matcher));
-
- // Also very empty and null values
- BytesMatcher.decode("");
- BytesMatcher.decode(null);
- }
-
- @Test
- public void testSerialize_Exact() throws Exception {
- BytesMatcher matcher = new BytesMatcher();
- matcher.addExactRejectRule(hexStringToByteArray("cafe00112233"),
- hexStringToByteArray("ffffff000000"));
- matcher.addExactRejectRule(hexStringToByteArray("beef00112233"),
- null);
- matcher.addExactAcceptRule(hexStringToByteArray("000000000000"),
- hexStringToByteArray("000000000000"));
-
- assertFalse(matcher.test(hexStringToByteArray("cafe00ffffff")));
- assertFalse(matcher.test(hexStringToByteArray("beef00112233")));
- assertTrue(matcher.test(hexStringToByteArray("beef00ffffff")));
-
- // Bounce through serialization pass and confirm it still works
- matcher = BytesMatcher.decode(BytesMatcher.encode(matcher));
-
- assertFalse(matcher.test(hexStringToByteArray("cafe00ffffff")));
- assertFalse(matcher.test(hexStringToByteArray("beef00112233")));
- assertTrue(matcher.test(hexStringToByteArray("beef00ffffff")));
- }
-
- @Test
- public void testSerialize_Prefix() throws Exception {
- BytesMatcher matcher = new BytesMatcher();
- matcher.addExactRejectRule(hexStringToByteArray("aa"), null);
- matcher.addExactAcceptRule(hexStringToByteArray("bb"), null);
- matcher.addPrefixAcceptRule(hexStringToByteArray("aa"), null);
- matcher.addPrefixRejectRule(hexStringToByteArray("bb"), null);
-
- assertFalse(matcher.test(hexStringToByteArray("aa")));
- assertTrue(matcher.test(hexStringToByteArray("bb")));
- assertTrue(matcher.test(hexStringToByteArray("aaaa")));
- assertFalse(matcher.test(hexStringToByteArray("bbbb")));
-
- // Bounce through serialization pass and confirm it still works
- matcher = BytesMatcher.decode(BytesMatcher.encode(matcher));
-
- assertFalse(matcher.test(hexStringToByteArray("aa")));
- assertTrue(matcher.test(hexStringToByteArray("bb")));
- assertTrue(matcher.test(hexStringToByteArray("aaaa")));
- assertFalse(matcher.test(hexStringToByteArray("bbbb")));
- }
-
- @Test
- public void testOrdering_RejectFirst() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("-ff/0f,+ff/f0");
- assertFalse(matcher.test(hexStringToByteArray("ff")));
- assertTrue(matcher.test(hexStringToByteArray("f0")));
- assertFalse(matcher.test(hexStringToByteArray("0f")));
- }
-
- @Test
- public void testOrdering_AcceptFirst() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+ff/f0,-ff/0f");
- assertTrue(matcher.test(hexStringToByteArray("ff")));
- assertTrue(matcher.test(hexStringToByteArray("f0")));
- assertFalse(matcher.test(hexStringToByteArray("0f")));
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java
index 3c093d8..2c31b08 100644
--- a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java
+++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java
@@ -30,6 +30,7 @@
import com.android.internal.content.om.OverlayConfig.PackageProvider;
import com.android.internal.content.om.OverlayScanner;
import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
+import com.android.internal.util.function.TriConsumer;
import org.junit.Assert;
import org.junit.rules.TestRule;
@@ -42,7 +43,6 @@
import java.io.IOException;
import java.util.List;
import java.util.Map;
-import java.util.function.BiConsumer;
import java.util.function.Supplier;
/**
@@ -74,7 +74,7 @@
TestOverlayInfo(String packageName, String targetPackageName,
int targetSdkVersion, boolean isStatic, int priority, File path,
String requiredSystemPropertyName, String requiredSystemPropertyValue) {
- super(packageName, targetPackageName, targetSdkVersion, isStatic, priority, path);
+ super(packageName, targetPackageName, targetSdkVersion, isStatic, priority, path, null);
this.requiredSystemPropertyName = requiredSystemPropertyName;
this.requiredSystemPropertyValue = requiredSystemPropertyValue;
}
@@ -174,8 +174,8 @@
mIteration = Iteration.SYSTEM_SERVER;
doAnswer((InvocationOnMock invocation) -> {
final Object[] args = invocation.getArguments();
- final BiConsumer<ParsingPackageRead, Boolean> f =
- (BiConsumer<ParsingPackageRead, Boolean>) args[0];
+ final TriConsumer<ParsingPackageRead, Boolean, File> f =
+ (TriConsumer<ParsingPackageRead, Boolean, File>) args[0];
for (Map.Entry<File, TestOverlayInfo> overlay :
mTestOverlayInfos.entrySet()) {
final ParsingPackageRead a = Mockito.mock(ParsingPackageRead.class);
@@ -191,7 +191,8 @@
when(a.isOverlayIsStatic()).thenReturn(info.isStatic);
when(a.getOverlayPriority()).thenReturn(info.priority);
when(a.getBaseApkPath()).thenReturn(info.path.getPath());
- f.accept(a, !info.path.getPath().contains("data/overlay"));
+ f.accept(a, !info.path.getPath().contains("data/overlay"),
+ /*preInstalledApexPath=*/null);
}
return null;
}).when(mPkgProvider).forEachPackage(any());
diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
index 5c84794..d361da9 100644
--- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
@@ -33,6 +33,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class BluetoothPowerCalculatorTest {
@@ -105,10 +107,10 @@
final BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 7000, 5000, 0,
reportedEnergyUc);
- info.setUidTraffic(new UidTraffic[]{
- new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
- new UidTraffic(APP_UID, 3000, 4000)
- });
+ info.setUidTraffic(new ArrayList<UidTraffic>(){{
+ add(new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000));
+ add(new UidTraffic(APP_UID, 3000, 4000));
+ }});
mStatsRule.getBatteryStats().updateBluetoothStateLocked(info,
consumedEnergyUc, 1000, 1000);
}
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 c24dc67..d16689c 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -65,8 +65,9 @@
final boolean[] supportedStandardBuckets =
new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
Arrays.fill(supportedStandardBuckets, true);
- mGlobalMeasuredEnergyStats =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
+ mMeasuredEnergyStatsConfig = new MeasuredEnergyStats.Config(supportedStandardBuckets,
+ customBucketNames, new int[0], new String[]{""});
+ mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(mMeasuredEnergyStatsConfig);
}
public TimeBase getOnBatteryTimeBase() {
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index a70033b..dc5bc97 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -19,9 +19,12 @@
import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
import static com.android.internal.power.MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS;
+import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH;
+import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_CPU;
import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE;
import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON;
import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_SCREEN_OTHER;
+import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_WIFI;
import static com.google.common.truth.Truth.assertThat;
@@ -56,55 +59,32 @@
supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
+ final MeasuredEnergyStats.Config config =
+ new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+ new int[]{POWER_BUCKET_SCREEN_ON, POWER_BUCKET_WIFI},
+ new String[]{"state0", "state1", "state3"});
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
- for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) {
- if (supportedStandardBuckets[i]) {
- assertTrue(stats.isStandardBucketSupported(i));
- assertEquals(0L, stats.getAccumulatedStandardBucketCharge(i));
+ for (int bucket = 0; bucket < NUMBER_STANDARD_POWER_BUCKETS; bucket++) {
+ if (supportedStandardBuckets[bucket]) {
+ assertTrue(stats.isStandardBucketSupported(bucket));
+ assertEquals(0L, stats.getAccumulatedStandardBucketCharge(bucket));
} else {
- assertFalse(stats.isStandardBucketSupported(i));
- assertEquals(POWER_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketCharge(i));
+ assertFalse(stats.isStandardBucketSupported(bucket));
+ assertEquals(POWER_DATA_UNAVAILABLE,
+ stats.getAccumulatedStandardBucketCharge(bucket));
+ }
+ if (bucket == POWER_BUCKET_SCREEN_ON) {
+ assertThat(config.isSupportedMultiStateBucket(bucket)).isTrue();
+ } else {
+ assertThat(config.isSupportedMultiStateBucket(bucket)).isFalse();
}
}
for (int i = 0; i < customBucketNames.length; i++) {
assertEquals(0L, stats.getAccumulatedCustomBucketCharge(i));
}
- assertThat(stats.getCustomBucketNames()).asList().containsExactly("A", "B");
- }
-
- @Test
- public void testCreateFromTemplate() {
- final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
- final String[] customBucketNames = {"A", "B"};
- supportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true;
- supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
- supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
-
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
- stats.updateCustomBucket(0, 50);
- stats.updateCustomBucket(1, 60);
-
- final MeasuredEnergyStats newStats = MeasuredEnergyStats.createFromTemplate(stats);
-
- for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) {
- if (supportedStandardBuckets[i]) {
- assertTrue(newStats.isStandardBucketSupported(i));
- assertEquals(0L, newStats.getAccumulatedStandardBucketCharge(i));
- } else {
- assertFalse(newStats.isStandardBucketSupported(i));
- assertEquals(POWER_DATA_UNAVAILABLE,
- newStats.getAccumulatedStandardBucketCharge(i));
- }
- }
- for (int i = 0; i < customBucketNames.length; i++) {
- assertEquals(0L, newStats.getAccumulatedCustomBucketCharge(i));
- }
+ assertThat(config.getCustomBucketNames()).asList().containsExactly("A", "B");
+ assertThat(config.getStateNames()).asList().containsExactly("state0", "state1", "state3");
}
@Test
@@ -115,23 +95,32 @@
supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
- stats.updateCustomBucket(0, 50);
- stats.updateCustomBucket(1, 60);
+ final MeasuredEnergyStats.Config config =
+ new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+ new int[]{POWER_BUCKET_SCREEN_ON}, new String[]{"s0", "s1"});
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+
+ stats.setState(0, 1000);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10, 2000);
+ stats.setState(1, 3000);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5, 4000);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40, 5000);
+ stats.updateCustomBucket(0, 50, 6000);
+ stats.updateCustomBucket(1, 60, 7000);
final Parcel parcel = Parcel.obtain();
stats.writeToParcel(parcel);
parcel.setDataPosition(0);
- MeasuredEnergyStats newStats = new MeasuredEnergyStats(parcel);
+ MeasuredEnergyStats newStats = new MeasuredEnergyStats(config, parcel);
- for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) {
- assertEquals(stats.getAccumulatedStandardBucketCharge(i),
- newStats.getAccumulatedStandardBucketCharge(i));
+ for (int bucket = 0; bucket < NUMBER_STANDARD_POWER_BUCKETS; bucket++) {
+ assertEquals(stats.getAccumulatedStandardBucketCharge(bucket),
+ newStats.getAccumulatedStandardBucketCharge(bucket));
+ for (int state = 0; state < 2; state++) {
+ assertEquals(stats.getAccumulatedStandardBucketCharge(bucket, state),
+ newStats.getAccumulatedStandardBucketCharge(bucket, state));
+ }
}
for (int i = 0; i < customBucketNames.length; i++) {
assertEquals(stats.getAccumulatedCustomBucketCharge(i),
@@ -143,6 +132,57 @@
}
@Test
+ public void testCreateAndReadConfigFromParcel() {
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
+ final String[] customBucketNames = {"A", "B"};
+ supportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
+
+ final MeasuredEnergyStats.Config config =
+ new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+ new int[]{POWER_BUCKET_SCREEN_ON, POWER_BUCKET_WIFI},
+ new String[] {"state0", "state1", "state2"});
+
+ final Parcel parcel = Parcel.obtain();
+ MeasuredEnergyStats.Config.writeToParcel(config, parcel);
+
+ parcel.setDataPosition(0);
+
+ final MeasuredEnergyStats.Config newConfig = MeasuredEnergyStats.Config.createFromParcel(
+ parcel);
+
+ assertThat(newConfig).isNotNull();
+ for (int bucket = 0; bucket < NUMBER_STANDARD_POWER_BUCKETS; bucket++) {
+ if (bucket == POWER_BUCKET_SCREEN_ON || bucket == POWER_BUCKET_SCREEN_OTHER) {
+ assertThat(newConfig.isSupportedBucket(bucket)).isTrue();
+ } else {
+ assertThat(newConfig.isSupportedBucket(bucket)).isFalse();
+ }
+ if (bucket == POWER_BUCKET_SCREEN_ON) {
+ assertThat(newConfig.isSupportedMultiStateBucket(bucket)).isTrue();
+ } else {
+ assertThat(newConfig.isSupportedMultiStateBucket(bucket)).isFalse();
+ }
+ }
+ assertThat(newConfig.getCustomBucketNames()).isEqualTo(new String[]{"A", "B"});
+ assertThat(newConfig.getStateNames()).isEqualTo(new String[]{"state0", "state1", "state2"});
+ }
+
+ @Test
+ public void testCreateAndReadConfigFromParcel_nullConfig() {
+ final Parcel parcel = Parcel.obtain();
+ MeasuredEnergyStats.Config.writeToParcel(null, parcel);
+
+ parcel.setDataPosition(0);
+
+ final MeasuredEnergyStats.Config newConfig = MeasuredEnergyStats.Config.createFromParcel(
+ parcel);
+
+ assertThat(newConfig).isNull();
+ }
+
+ @Test
public void testCreateAndReadSummaryFromParcel() {
final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
final String[] customBucketNames = {"A", "B"};
@@ -150,8 +190,11 @@
supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
+ final MeasuredEnergyStats.Config config =
+ new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+ new int[0], new String[]{"s"});
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+
stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
@@ -159,9 +202,12 @@
stats.updateCustomBucket(1, 60);
final Parcel parcel = Parcel.obtain();
- MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false, false);
+ MeasuredEnergyStats.writeSummaryToParcel(stats, parcel);
+
parcel.setDataPosition(0);
- MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel);
+
+ MeasuredEnergyStats newStats =
+ MeasuredEnergyStats.createAndReadSummaryFromParcel(config, parcel);
for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) {
assertEquals(stats.isStandardBucketSupported(i),
@@ -175,48 +221,44 @@
}
assertEquals(POWER_DATA_UNAVAILABLE,
newStats.getAccumulatedCustomBucketCharge(customBucketNames.length + 1));
- assertThat(newStats.getCustomBucketNames()).asList().containsExactly("A", "B");
parcel.recycle();
}
@Test
- public void testCreateAndReadSummaryFromParcel_existingTemplate() {
+ public void testCreateAndReadSummaryFromParcel_configChange() {
final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
final String[] customBucketNames = {"A", "B"};
supportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true;
supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats template =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
- template.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
- template.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
- template.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
- template.updateCustomBucket(0, 50);
-
- final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 200);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 7);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 63);
- stats.updateCustomBucket(0, 315);
- stats.updateCustomBucket(1, 316);
+ final MeasuredEnergyStats.Config config =
+ new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+ new int[0], new String[]{"s"});
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
+ stats.updateCustomBucket(0, 50);
final Parcel parcel = Parcel.obtain();
- MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false, true);
-
- final boolean[] newsupportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
- newsupportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true;
- newsupportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = true; // switched false > true
- newsupportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = false; // switched true > false
- final MeasuredEnergyStats newTemplate =
- new MeasuredEnergyStats(newsupportedStandardBuckets, customBucketNames);
+ MeasuredEnergyStats.writeSummaryToParcel(stats, parcel);
parcel.setDataPosition(0);
+ final boolean[] newSupportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
+ newSupportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true;
+ newSupportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = true; // switched false > true
+ newSupportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = false; // switched true > false
+
+ final MeasuredEnergyStats.Config newConfig =
+ new MeasuredEnergyStats.Config(newSupportedStandardBuckets, customBucketNames,
+ new int[0], new String[]{"s"});
+
final MeasuredEnergyStats newStats =
- MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, newTemplate);
+ MeasuredEnergyStats.createAndReadSummaryFromParcel(newConfig, parcel);
for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) {
- if (!newsupportedStandardBuckets[i]) {
+ if (!newSupportedStandardBuckets[i]) {
assertFalse(newStats.isStandardBucketSupported(i));
assertEquals(POWER_DATA_UNAVAILABLE,
newStats.getAccumulatedStandardBucketCharge(i));
@@ -235,81 +277,22 @@
}
assertEquals(POWER_DATA_UNAVAILABLE,
newStats.getAccumulatedCustomBucketCharge(customBucketNames.length + 1));
- assertThat(newStats.getCustomBucketNames()).asList().containsExactly("A", "B");
parcel.recycle();
}
@Test
- public void testCreateAndReadSummaryFromParcel_skipZero() {
- final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
- final String[] customBucketNames = {"A", "B"};
- Arrays.fill(supportedStandardBuckets, true);
-
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
- // Accumulate charge in one bucket and one custom bucket, the rest should be zero
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 200);
- stats.updateCustomBucket(1, 60);
-
- // Let's try parcelling with including zeros
- final Parcel includeZerosParcel = Parcel.obtain();
- MeasuredEnergyStats.writeSummaryToParcel(stats, includeZerosParcel, false, false);
- includeZerosParcel.setDataPosition(0);
-
- MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(
- includeZerosParcel);
-
- for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) {
- if (i == POWER_BUCKET_SCREEN_ON) {
- assertEquals(stats.isStandardBucketSupported(i),
- newStats.isStandardBucketSupported(i));
- assertEquals(stats.getAccumulatedStandardBucketCharge(i),
- newStats.getAccumulatedStandardBucketCharge(i));
- } else {
- assertTrue(newStats.isStandardBucketSupported(i));
- assertEquals(0L, newStats.getAccumulatedStandardBucketCharge(i));
- }
- }
- assertEquals(0L, newStats.getAccumulatedCustomBucketCharge(0));
- assertEquals(stats.getAccumulatedCustomBucketCharge(1),
- newStats.getAccumulatedCustomBucketCharge(1));
- includeZerosParcel.recycle();
-
- // Now let's try parcelling with skipping zeros
- final Parcel skipZerosParcel = Parcel.obtain();
- MeasuredEnergyStats.writeSummaryToParcel(stats, skipZerosParcel, true, false);
- skipZerosParcel.setDataPosition(0);
-
- newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(skipZerosParcel);
-
- for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) {
- if (i == POWER_BUCKET_SCREEN_ON) {
- assertEquals(stats.isStandardBucketSupported(i),
- newStats.isStandardBucketSupported(i));
- assertEquals(stats.getAccumulatedStandardBucketCharge(i),
- newStats.getAccumulatedStandardBucketCharge(i));
- } else {
- assertFalse(newStats.isStandardBucketSupported(i));
- assertEquals(POWER_DATA_UNAVAILABLE,
- newStats.getAccumulatedStandardBucketCharge(i));
- }
- }
- assertEquals(0L, newStats.getAccumulatedCustomBucketCharge(0));
- assertEquals(stats.getAccumulatedCustomBucketCharge(1),
- newStats.getAccumulatedCustomBucketCharge(1));
- skipZerosParcel.recycle();
- }
-
- @Test
- public void testCreateAndReadSummaryFromParcel_nullTemplate() {
+ public void testCreateAndReadSummaryFromParcel_nullConfig() {
final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
final String[] customBucketNames = {"A", "B"};
supportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true;
supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
+ final MeasuredEnergyStats.Config config =
+ new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+ new int[0], new String[]{"s"});
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+
stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
@@ -317,11 +300,11 @@
stats.updateCustomBucket(1, 60);
final Parcel parcel = Parcel.obtain();
- MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false, true);
+ MeasuredEnergyStats.writeSummaryToParcel(stats, parcel);
parcel.setDataPosition(0);
MeasuredEnergyStats newStats =
- MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, null);
+ MeasuredEnergyStats.createAndReadSummaryFromParcel(null, parcel);
assertNull(newStats);
parcel.recycle();
}
@@ -334,30 +317,30 @@
supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats template =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
- template.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
- template.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
- template.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
- template.updateCustomBucket(0, 50);
+ final MeasuredEnergyStats.Config config =
+ new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+ new int[0], new String[]{"s"});
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
- final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 0L);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 7L);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 0);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
final Parcel parcel = Parcel.obtain();
- MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false, true);
+ MeasuredEnergyStats.writeSummaryToParcel(stats, parcel);
final boolean[] newSupportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
newSupportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true;
newSupportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = true; // switched false > true
newSupportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = false; // switched true > false
- final MeasuredEnergyStats newTemplate =
- new MeasuredEnergyStats(newSupportedStandardBuckets, customBucketNames);
+ final MeasuredEnergyStats.Config newConfig =
+ new MeasuredEnergyStats.Config(newSupportedStandardBuckets, customBucketNames,
+ new int[0], new String[]{"s"});
+
parcel.setDataPosition(0);
final MeasuredEnergyStats newStats =
- MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, newTemplate);
+ MeasuredEnergyStats.createAndReadSummaryFromParcel(newConfig, parcel);
+
// The only non-0 entry in stats is no longer supported, so now there's no interesting data.
assertNull(newStats);
assertEquals("Parcel was not properly consumed", 0, parcel.dataAvail());
@@ -372,30 +355,47 @@
supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_DOZE, 30);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
+ final MeasuredEnergyStats.Config config =
+ new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+ new int[]{POWER_BUCKET_SCREEN_ON, POWER_BUCKET_SCREEN_OTHER},
+ new String[]{"s0", "s1"});
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
- stats.updateCustomBucket(0, 50);
- stats.updateCustomBucket(1, 60);
- stats.updateCustomBucket(0, 3);
+ stats.setState(0, 1000);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10, 2000);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_DOZE, 30, 3000);
+ stats.setState(1, 4000);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40, 5000);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 6, 6000);
- assertEquals(15, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON));
+ stats.updateCustomBucket(0, 50, 7000);
+ stats.updateCustomBucket(1, 60, 8000);
+ stats.updateCustomBucket(0, 3, 9000);
+
+ assertEquals(16, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON));
assertEquals(POWER_DATA_UNAVAILABLE,
stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_DOZE));
assertEquals(40, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER));
assertEquals(50 + 3, stats.getAccumulatedCustomBucketCharge(0));
assertEquals(60, stats.getAccumulatedCustomBucketCharge(1));
+
+ // 10 + 6 * (4000-2000)/(6000-2000)
+ assertEquals(13, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 0));
+ // 6 * (6000-4000)/(6000-2000)
+ assertEquals(3, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 1));
+
+ // POWER_BUCKET_SCREEN_OTHER was only present along with state=1
+ assertEquals(0, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 0));
+ assertEquals(40, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 1));
}
@Test
public void testIsValidCustomBucket() {
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
- new String[]{"A", "B", "C"});
+ final MeasuredEnergyStats.Config config =
+ new MeasuredEnergyStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
+ new String[]{"A", "B", "C"},
+ new int[0], new String[]{"s"});
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
assertFalse(stats.isValidCustomBucket(-1));
assertTrue(stats.isValidCustomBucket(0));
assertTrue(stats.isValidCustomBucket(1));
@@ -403,8 +403,10 @@
assertFalse(stats.isValidCustomBucket(3));
assertFalse(stats.isValidCustomBucket(4));
- final MeasuredEnergyStats boringStats =
- new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], new String[0]);
+ final MeasuredEnergyStats.Config boringConfig =
+ new MeasuredEnergyStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
+ new String[0], new int[0], new String[]{"s"});
+ final MeasuredEnergyStats boringStats = new MeasuredEnergyStats(boringConfig);
assertFalse(boringStats.isValidCustomBucket(-1));
assertFalse(boringStats.isValidCustomBucket(0));
assertFalse(boringStats.isValidCustomBucket(1));
@@ -412,9 +414,11 @@
@Test
public void testGetAccumulatedCustomBucketCharges() {
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
- new String[]{"A", "B", "C"});
+ final MeasuredEnergyStats.Config config =
+ new MeasuredEnergyStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
+ new String[]{"A", "B", "C"},
+ new int[0], new String[]{"s"});
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
stats.updateCustomBucket(0, 50);
stats.updateCustomBucket(1, 60);
stats.updateCustomBucket(2, 13);
@@ -430,8 +434,10 @@
@Test
public void testGetAccumulatedCustomBucketCharges_empty() {
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], new String[0]);
+ final MeasuredEnergyStats.Config config =
+ new MeasuredEnergyStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
+ new String[0], new int[0], new String[]{"s"});
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
final long[] output = stats.getAccumulatedCustomBucketCharges();
assertEquals(0, output.length);
@@ -440,10 +446,15 @@
@Test
public void testGetNumberCustomChargeBuckets() {
assertEquals(0,
- new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], new String[0])
+ new MeasuredEnergyStats(
+ new MeasuredEnergyStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
+ new String[0], new int[0], new String[]{"s"}))
.getNumberCustomPowerBuckets());
- assertEquals(3, new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
- new String[]{"A", "B", "C"}).getNumberCustomPowerBuckets());
+ assertEquals(3,
+ new MeasuredEnergyStats(
+ new MeasuredEnergyStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
+ new String[]{"A", "B", "C"}, new int[0], new String[]{"s"}))
+ .getNumberCustomPowerBuckets());
}
@Test
@@ -454,8 +465,10 @@
supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
+ final MeasuredEnergyStats.Config config =
+ new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+ new int[0], new String[]{"s"});
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
@@ -500,83 +513,62 @@
assertEquals(exp, MeasuredEnergyStats.getDisplayPowerBucket(Display.STATE_DOZE_SUSPEND));
}
- /** Test MeasuredEnergyStats#isSupportEqualTo */
@Test
- public void testIsSupportEqualTo() {
+ public void testConfig_isCompatible() {
final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
Arrays.fill(supportedStandardBuckets, true);
final String[] customBucketNames = {"A", "B"};
+ final int[] supportedMultiStateBuckets = {POWER_BUCKET_CPU, POWER_BUCKET_WIFI};
+ final String[] stateNames = {"s"};
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(supportedStandardBuckets.clone(),
- customBucketNames.clone());
-
+ final MeasuredEnergyStats.Config config = new MeasuredEnergyStats.Config(
+ supportedStandardBuckets,
+ customBucketNames,
+ supportedMultiStateBuckets,
+ stateNames);
assertTrue(
"All standard and custom bucket supports match",
- stats.isSupportEqualTo(supportedStandardBuckets, customBucketNames));
+ config.isCompatible(
+ new MeasuredEnergyStats.Config(
+ supportedStandardBuckets,
+ customBucketNames,
+ supportedMultiStateBuckets,
+ stateNames)));
boolean[] differentSupportedStandardBuckets = supportedStandardBuckets.clone();
differentSupportedStandardBuckets[0] = !differentSupportedStandardBuckets[0];
+
assertFalse(
"Standard bucket support mismatch",
- stats.isSupportEqualTo(differentSupportedStandardBuckets, customBucketNames));
-
+ config.isCompatible(
+ new MeasuredEnergyStats.Config(
+ differentSupportedStandardBuckets,
+ customBucketNames,
+ supportedMultiStateBuckets,
+ stateNames)));
assertFalse(
"Custom bucket support mismatch",
- stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"C", "B"}));
-
+ config.isCompatible(
+ new MeasuredEnergyStats.Config(
+ supportedStandardBuckets,
+ new String[]{"C", "B"},
+ supportedMultiStateBuckets,
+ stateNames)));
assertFalse(
- "Fewer custom buckets supported",
- stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A"}));
-
+ "Multi-state bucket mismatch",
+ config.isCompatible(
+ new MeasuredEnergyStats.Config(
+ supportedStandardBuckets,
+ new String[]{"A"},
+ new int[] {POWER_BUCKET_CPU, POWER_BUCKET_BLUETOOTH},
+ stateNames)));
assertFalse(
- "More custom bucket supported",
- stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A", "B", "C"}));
-
- assertFalse(
- "Custom bucket support order changed",
- stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"B", "A"}));
- }
-
- /** Test MeasuredEnergyStats#isSupportEqualTo when holding a null array of custom buckets */
- @Test
- public void testIsSupportEqualTo_nullCustomBuckets() {
- final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
-
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(supportedStandardBuckets.clone(), null);
-
- assertTrue(
- "Null custom bucket name lists should match",
- stats.isSupportEqualTo(supportedStandardBuckets, null));
-
- assertTrue(
- "Null and empty custom buckets should match",
- stats.isSupportEqualTo(supportedStandardBuckets, new String[0]));
-
- assertFalse(
- "Null custom buckets should not match populated list",
- stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A", "B"}));
- }
-
- /** Test MeasuredEnergyStats#isSupportEqualTo when holding an empty array of custom buckets */
- @Test
- public void testIsSupportEqualTo_emptyCustomBuckets() {
- final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
-
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(supportedStandardBuckets.clone(), new String[0]);
-
- assertTrue(
- "Empty custom buckets should match",
- stats.isSupportEqualTo(supportedStandardBuckets, new String[0]));
-
- assertTrue(
- "Empty and null custom buckets should match",
- stats.isSupportEqualTo(supportedStandardBuckets, null));
-
- assertFalse(
- "Empty custom buckets should not match populated list",
- stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A", "B"}));
+ "Multi-state bucket state list mismatch",
+ config.isCompatible(
+ new MeasuredEnergyStats.Config(
+ supportedStandardBuckets,
+ new String[]{"A"},
+ supportedMultiStateBuckets,
+ new String[]{"s1", "s2"})));
}
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 6e92755..8b37805 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1561,6 +1561,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayRotation.java"
},
+ "-436553282": {
+ "message": "Remove sleep token: tag=%s, displayId=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
"-415865166": {
"message": "findFocusedWindow: Found new focus @ %s",
"level": "VERBOSE",
@@ -1669,6 +1675,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-317761482": {
+ "message": "Create sleep token: tag=%s, displayId=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
"-317194205": {
"message": "clearLockedTasks: %s",
"level": "INFO",
diff --git a/libs/WindowManager/Shell/res/layout/split_decor.xml b/libs/WindowManager/Shell/res/layout/split_decor.xml
new file mode 100644
index 0000000..9ffa5e8
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/split_decor.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ 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.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <ImageView android:id="@+id/split_resizing_icon"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center"
+ android:padding="0dp"
+ android:visibility="gone"
+ android:background="@null"/>
+
+</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/layout/split_outline.xml b/libs/WindowManager/Shell/res/layout/split_outline.xml
index 13a30f5..6cb9ebb 100644
--- a/libs/WindowManager/Shell/res/layout/split_outline.xml
+++ b/libs/WindowManager/Shell/res/layout/split_outline.xml
@@ -1,18 +1,19 @@
<?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.
--->
+<!--
+ ~ 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.
+ -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index 9113c79..e87b150 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -103,8 +103,6 @@
return runMoveToSideStage(args, pw);
case "removeFromSideStage":
return runRemoveFromSideStage(args, pw);
- case "setSideStageOutline":
- return runSetSideStageOutline(args, pw);
case "setSideStagePosition":
return runSetSideStagePosition(args, pw);
case "setSideStageVisibility":
@@ -163,18 +161,6 @@
return true;
}
- private boolean runSetSideStageOutline(String[] args, PrintWriter pw) {
- if (args.length < 3) {
- // First arguments are "WMShell" and command name.
- pw.println("Error: whether to enable or disable side stage outline border should be"
- + " provided as arguments");
- return false;
- }
- final boolean enable = new Boolean(args[2]);
- mSplitScreenOptional.ifPresent(split -> split.setSideStageOutline(enable));
- return true;
- }
-
private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
if (args.length < 3) {
// First arguments are "WMShell" and command name.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index 6a252e0..c8449a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -131,7 +131,8 @@
mSplitLayout = new SplitLayout(TAG + "SplitDivider",
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
mRootTaskInfo.configuration, this /* layoutChangeListener */,
- mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer());
+ mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer(),
+ true /* applyDismissingParallax */);
mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout);
final WindowContainerToken token1 = task1.token;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java
new file mode 100644
index 0000000..dc20f7b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java
@@ -0,0 +1,41 @@
+/*
+ * 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.wm.shell.back;
+
+import android.os.SystemProperties;
+import android.view.IWindowManager;
+
+import javax.inject.Inject;
+
+/**
+ * Handle the preview of what a back gesture will lead to.
+ */
+public class BackPreviewHandler {
+
+ private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+
+ public static boolean isEnabled() {
+ return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
+ }
+
+ private final IWindowManager mWmService;
+
+ @Inject
+ public BackPreviewHandler(IWindowManager windowManagerService) {
+ mWmService = windowManagerService;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java
index 55c5125..4b138e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java
@@ -23,16 +23,22 @@
* Helpers for handling surface.
*/
public class SurfaceUtils {
- /** Creates a dim layer above indicated host surface. */
+ /** Creates a dim layer above host surface. */
public static SurfaceControl makeDimLayer(SurfaceControl.Transaction t, SurfaceControl host,
String name, SurfaceSession surfaceSession) {
- SurfaceControl dimLayer = new SurfaceControl.Builder(surfaceSession)
+ final SurfaceControl dimLayer = makeColorLayer(host, name, surfaceSession);
+ t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f});
+ return dimLayer;
+ }
+
+ /** Creates a color layer for host surface. */
+ public static SurfaceControl makeColorLayer(SurfaceControl host, String name,
+ SurfaceSession surfaceSession) {
+ return new SurfaceControl.Builder(surfaceSession)
.setParent(host)
.setColorLayer()
.setName(name)
- .setCallsite("SurfaceUtils.makeDimLayer")
+ .setCallsite("SurfaceUtils.makeColorLayer")
.build();
- t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f});
- return dimLayer;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
new file mode 100644
index 0000000..ad9ebb2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -0,0 +1,184 @@
+/*
+ * 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.wm.shell.common.split;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Binder;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.SurfaceUtils;
+
+/**
+ * Handles split decor like showing resizing hint for a specific split.
+ */
+public class SplitDecorManager extends WindowlessWindowManager {
+ private static final String TAG = SplitDecorManager.class.getSimpleName();
+ private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground";
+
+ private final IconProvider mIconProvider;
+ private final SurfaceSession mSurfaceSession;
+
+ private Drawable mIcon;
+ private ImageView mResizingIconView;
+ private SurfaceControlViewHost mViewHost;
+ private SurfaceControl mHostLeash;
+ private SurfaceControl mIconLeash;
+ private SurfaceControl mBackgroundLeash;
+
+ public SplitDecorManager(Configuration configuration, IconProvider iconProvider,
+ SurfaceSession surfaceSession) {
+ super(configuration, null /* rootSurface */, null /* hostInputToken */);
+ mIconProvider = iconProvider;
+ mSurfaceSession = surfaceSession;
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName(TAG)
+ .setHidden(true)
+ .setParent(mHostLeash)
+ .setCallsite("SplitDecorManager#attachToParentSurface");
+ mIconLeash = builder.build();
+ b.setParent(mIconLeash);
+ }
+
+ /** Inflates split decor surface on the root surface. */
+ public void inflate(Context context, SurfaceControl rootLeash, Rect rootBounds) {
+ if (mIconLeash != null && mViewHost != null) {
+ return;
+ }
+
+ context = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
+ null /* options */);
+ mHostLeash = rootLeash;
+ mViewHost = new SurfaceControlViewHost(context, context.getDisplay(), this);
+
+ final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(context)
+ .inflate(R.layout.split_decor, null);
+ mResizingIconView = rootLayout.findViewById(R.id.split_resizing_icon);
+
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
+ lp.width = rootBounds.width();
+ lp.height = rootBounds.height();
+ lp.token = new Binder();
+ lp.setTitle(TAG);
+ lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
+ // TRUSTED_OVERLAY for windowless window without input channel.
+ mViewHost.setView(rootLayout, lp);
+ }
+
+ /** Releases the surfaces for split decor. */
+ public void release(SurfaceControl.Transaction t) {
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+ if (mIconLeash != null) {
+ t.remove(mIconLeash);
+ mIconLeash = null;
+ }
+ if (mBackgroundLeash != null) {
+ t.remove(mBackgroundLeash);
+ mBackgroundLeash = null;
+ }
+ mHostLeash = null;
+ mIcon = null;
+ mResizingIconView = null;
+ }
+
+ /** Showing resizing hint. */
+ public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
+ SurfaceControl.Transaction t) {
+ if (mResizingIconView == null) {
+ return;
+ }
+
+ if (mIcon == null) {
+ // TODO: add fade-in animation.
+ mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
+ RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
+ t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
+ .setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1)
+ .show(mBackgroundLeash);
+
+ mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
+ mResizingIconView.setImageDrawable(mIcon);
+ mResizingIconView.setVisibility(View.VISIBLE);
+
+ WindowManager.LayoutParams lp =
+ (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
+ lp.width = mIcon.getIntrinsicWidth();
+ lp.height = mIcon.getIntrinsicHeight();
+ mViewHost.relayout(lp);
+ t.show(mIconLeash).setLayer(mIconLeash, SPLIT_DIVIDER_LAYER);
+ }
+
+ t.setPosition(mIconLeash,
+ newBounds.width() / 2 - mIcon.getIntrinsicWidth() / 2,
+ newBounds.height() / 2 - mIcon.getIntrinsicWidth() / 2);
+ }
+
+ /** Stops showing resizing hint. */
+ public void onResized(Rect newBounds, SurfaceControl.Transaction t) {
+ if (mResizingIconView == null) {
+ return;
+ }
+
+ if (mIcon != null) {
+ mResizingIconView.setVisibility(View.GONE);
+ mResizingIconView.setImageDrawable(null);
+ t.remove(mBackgroundLeash).hide(mIconLeash);
+ mIcon = null;
+ mBackgroundLeash = null;
+ }
+ }
+
+ private static float[] getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
+ final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
+ return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).getComponents();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 5b3ce2d..a9ed64c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -103,7 +103,7 @@
private final SplitWindowManager mSplitWindowManager;
private final DisplayImeController mDisplayImeController;
private final ImePositionProcessor mImePositionProcessor;
- private final DismissingParallaxPolicy mDismissingParallaxPolicy;
+ private final DismissingEffectPolicy mDismissingEffectPolicy;
private final ShellTaskOrganizer mTaskOrganizer;
private final InsetsState mInsetsState = new InsetsState();
@@ -119,7 +119,8 @@
public SplitLayout(String windowName, Context context, Configuration configuration,
SplitLayoutHandler splitLayoutHandler,
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
- DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) {
+ DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer,
+ boolean applyDismissingParallax) {
mContext = context.createConfigurationContext(configuration);
mOrientation = configuration.orientation;
mRotation = configuration.windowConfiguration.getRotation();
@@ -129,7 +130,7 @@
parentContainerCallbacks);
mTaskOrganizer = taskOrganizer;
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
- mDismissingParallaxPolicy = new DismissingParallaxPolicy();
+ mDismissingEffectPolicy = new DismissingEffectPolicy(applyDismissingParallax);
final Resources resources = context.getResources();
mDividerWindowWidth = resources.getDimensionPixelSize(
@@ -247,7 +248,7 @@
}
DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */);
DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */);
- mDismissingParallaxPolicy.applyDividerPosition(position, isLandscape);
+ mDismissingEffectPolicy.applyDividerPosition(position, isLandscape);
}
/** Inflates {@link DividerView} on the root surface. */
@@ -290,7 +291,6 @@
*/
void updateDivideBounds(int position) {
updateBounds(position);
- mSplitWindowManager.setResizingSplits(true);
mSplitLayoutHandler.onLayoutSizeChanging(this);
}
@@ -298,13 +298,11 @@
mDividePosition = position;
updateBounds(mDividePosition);
mSplitLayoutHandler.onLayoutSizeChanged(this);
- mSplitWindowManager.setResizingSplits(false);
}
/** Resets divider position. */
public void resetDividerPosition() {
mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
- mSplitWindowManager.setResizingSplits(false);
updateBounds(mDividePosition);
mWinToken1 = null;
mWinToken2 = null;
@@ -360,8 +358,8 @@
@VisibleForTesting
void flingDividePosition(int from, int to, @Nullable Runnable flingFinishedCallback) {
if (from == to) {
- // No animation run, it should stop resizing here.
- mSplitWindowManager.setResizingSplits(false);
+ // No animation run, still callback to stop resizing.
+ mSplitLayoutHandler.onLayoutSizeChanged(this);
return;
}
ValueAnimator animator = ValueAnimator
@@ -425,7 +423,7 @@
return;
}
- mDismissingParallaxPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2);
+ mDismissingEffectPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2);
}
/** Apply recorded task layout to the {@link WindowContainerTransaction}. */
@@ -543,7 +541,10 @@
* Calculates and applies proper dismissing parallax offset and dimming value to hint users
* dismissing gesture.
*/
- private class DismissingParallaxPolicy {
+ private class DismissingEffectPolicy {
+ /** Indicates whether to offset splitting bounds to hint dismissing progress or not. */
+ private final boolean mApplyParallax;
+
// The current dismissing side.
int mDismissingSide = DOCKED_INVALID;
@@ -553,6 +554,10 @@
// The dimming value to hint the dismissing side and progress.
float mDismissingDimValue = 0.0f;
+ DismissingEffectPolicy(boolean applyDismissingParallax) {
+ mApplyParallax = applyDismissingParallax;
+ }
+
/**
* Applies a parallax to the task to hint dismissing progress.
*
@@ -565,11 +570,11 @@
mDismissingDimValue = 0;
int totalDismissingDistance = 0;
- if (position <= mDividerSnapAlgorithm.getFirstSplitTarget().position) {
+ if (position < mDividerSnapAlgorithm.getFirstSplitTarget().position) {
mDismissingSide = isLandscape ? DOCKED_LEFT : DOCKED_TOP;
totalDismissingDistance = mDividerSnapAlgorithm.getDismissStartTarget().position
- mDividerSnapAlgorithm.getFirstSplitTarget().position;
- } else if (position >= mDividerSnapAlgorithm.getLastSplitTarget().position) {
+ } else if (position > mDividerSnapAlgorithm.getLastSplitTarget().position) {
mDismissingSide = isLandscape ? DOCKED_RIGHT : DOCKED_BOTTOM;
totalDismissingDistance = mDividerSnapAlgorithm.getLastSplitTarget().position
- mDividerSnapAlgorithm.getDismissEndTarget().position;
@@ -627,12 +632,14 @@
return false;
}
- t.setPosition(targetLeash,
- mTempRect.left + mDismissingParallaxOffset.x,
- mTempRect.top + mDismissingParallaxOffset.y);
- // Transform the screen-based split bounds to surface-based crop bounds.
- mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y);
- t.setWindowCrop(targetLeash, mTempRect);
+ if (mApplyParallax) {
+ t.setPosition(targetLeash,
+ mTempRect.left + mDismissingParallaxOffset.x,
+ mTempRect.top + mDismissingParallaxOffset.y);
+ // Transform the screen-based split bounds to surface-based crop bounds.
+ mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y);
+ t.setWindowCrop(targetLeash, mTempRect);
+ }
t.setAlpha(targetDimLayer, mDismissingDimValue)
.setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 47dceb3..08754d3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -25,7 +25,6 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
@@ -33,8 +32,6 @@
import android.graphics.Region;
import android.os.Binder;
import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
import android.view.IWindow;
import android.view.InsetsState;
import android.view.LayoutInflater;
@@ -59,7 +56,6 @@
private Context mContext;
private SurfaceControlViewHost mViewHost;
private SurfaceControl mLeash;
- private boolean mResizingSplits;
private DividerView mDividerView;
public interface ParentContainerCallbacks {
@@ -154,16 +150,6 @@
mDividerView.setInteractive(interactive);
}
- void setResizingSplits(boolean resizing) {
- if (resizing == mResizingSplits) return;
- try {
- ActivityTaskManager.getService().setSplitScreenResizing(resizing);
- mResizingSplits = resizing;
- } catch (RemoteException e) {
- Slog.w(TAG, "Error calling setSplitScreenResizing", e);
- }
- }
-
/**
* Gets {@link SurfaceControl} of the surface holding divider view. @return {@code null} if not
* feasible.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 38079af..90074371 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -689,6 +689,8 @@
pw.println(mUserId);
pw.print(innerPrefix + "isShortcutEnabled=");
pw.println(isShortcutEnabled());
+ pw.print(innerPrefix + "mIsSwipeToNotificationEnabled=");
+ pw.println(mIsSwipeToNotificationEnabled);
if (mBackgroundPanelOrganizer != null) {
mBackgroundPanelOrganizer.dump(pw);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index ff333c8c..2cb7d1b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -244,6 +244,8 @@
pw.println(TAG);
pw.print(innerPrefix + "isOneHandedModeEnable=");
pw.println(getSettingsOneHandedModeEnabled(resolver, userId));
+ pw.print(innerPrefix + "isSwipeToNotificationEnabled=");
+ pw.println(getSettingsSwipeToNotificationEnabled(resolver, userId));
pw.print(innerPrefix + "oneHandedTimeOut=");
pw.println(getSettingsOneHandedModeTimeout(resolver, userId));
pw.print(innerPrefix + "tapsAppToExit=");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 291cbb3..b6e5804 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1292,13 +1292,17 @@
}
Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
? mPipBoundsState.getBounds() : currentBounds;
+ final boolean existingAnimatorRunning = mPipAnimationController.getCurrentAnimator() != null
+ && mPipAnimationController.getCurrentAnimator().isRunning();
final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds,
sourceHintRect, direction, startingAngle, rotationDelta);
animator.setTransitionDirection(direction)
- .setPipAnimationCallback(mPipAnimationCallback)
.setPipTransactionHandler(mPipTransactionHandler)
.setDuration(durationMs);
+ if (!existingAnimatorRunning) {
+ animator.setPipAnimationCallback(mPipAnimationCallback);
+ }
if (isInPipDirection(direction)) {
// Similar to auto-enter-pip transition, we use content overlay when there is no
// source rect hint to enter PiP use bounds animation.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 328f3ed..b31e6e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -116,6 +116,12 @@
if (mExitTransition == transition || info.getType() == TRANSIT_EXIT_PIP) {
mExitTransition = null;
if (info.getChanges().size() == 1) {
+ if (mFinishCallback != null) {
+ mFinishCallback.onTransitionFinished(null, null);
+ mFinishCallback = null;
+ throw new RuntimeException("Previous callback not called, aborting exit PIP.");
+ }
+
final TransitionInfo.Change change = info.getChanges().get(0);
mFinishCallback = finishCallback;
startTransaction.apply();
@@ -129,6 +135,12 @@
}
if (info.getType() == TRANSIT_REMOVE_PIP) {
+ if (mFinishCallback != null) {
+ mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
+ mFinishCallback = null;
+ throw new RuntimeException("Previous callback not called, aborting remove PIP.");
+ }
+
startTransaction.apply();
finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
mPipBoundsState.getDisplayBounds());
@@ -159,6 +171,12 @@
return false;
}
+ if (mFinishCallback != null) {
+ mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
+ mFinishCallback = null;
+ throw new RuntimeException("Previous callback not called, aborting entering PIP.");
+ }
+
// Show the wallpaper if there is a wallpaper change.
if (wallpaper != null) {
startTransaction.show(wallpaper.getLeash());
@@ -231,7 +249,7 @@
if (tx != null) {
wct.setBoundsChangeTransaction(taskInfo.token, tx);
}
- mFinishCallback.onTransitionFinished(wct, null /* wctCallback */);
+ mFinishCallback.onTransitionFinished(wct, null /* callback */);
mFinishCallback = null;
}
finishResizeForMenu(destinationBounds);
@@ -240,7 +258,7 @@
@Override
public void forceFinishTransition() {
if (mFinishCallback == null) return;
- mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCallback */);
+ mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
mFinishCallback = null;
}
@@ -286,7 +304,6 @@
mPipBoundsState.setBounds(destinationBounds);
onFinishResize(taskInfo, destinationBounds, TRANSITION_DIRECTION_TO_PIP, null /* tx */);
sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
- mFinishCallback = null;
mPipTransitionState.setInSwipePipToHomeTransition(false);
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index a47a152..6440ef0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -19,11 +19,13 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import android.annotation.Nullable;
+import android.content.Context;
import android.graphics.Rect;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -37,11 +39,11 @@
private boolean mIsActive = false;
- MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
+ MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession,
+ SurfaceSession surfaceSession, IconProvider iconProvider,
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
- super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
+ super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
stageTaskUnfoldController);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java
deleted file mode 100644
index a459c8d..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java
+++ /dev/null
@@ -1,181 +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.wm.shell.splitscreen;
-
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.view.IWindow;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowlessWindowManager;
-import android.widget.FrameLayout;
-
-import com.android.wm.shell.R;
-
-/**
- * Handles drawing outline of the bounds of provided root surface. The outline will be drown with
- * the consideration of display insets like status bar, navigation bar and display cutout.
- */
-class OutlineManager extends WindowlessWindowManager {
- private static final String WINDOW_NAME = "SplitOutlineLayer";
- private final Context mContext;
- private final Rect mRootBounds = new Rect();
- private final Rect mTempRect = new Rect();
- private final Rect mLastOutlineBounds = new Rect();
- private final InsetsState mInsetsState = new InsetsState();
- private final int mExpandedTaskBarHeight;
- private OutlineView mOutlineView;
- private SurfaceControlViewHost mViewHost;
- private SurfaceControl mHostLeash;
- private SurfaceControl mLeash;
-
- OutlineManager(Context context, Configuration configuration) {
- super(configuration, null /* rootSurface */, null /* hostInputToken */);
- mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
- null /* options */);
- mExpandedTaskBarHeight = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.taskbar_frame_height);
- }
-
- @Override
- protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
- b.setParent(mHostLeash);
- }
-
- void inflate(SurfaceControl rootLeash, Rect rootBounds) {
- if (mLeash != null || mViewHost != null) return;
-
- mHostLeash = rootLeash;
- mRootBounds.set(rootBounds);
- mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
-
- final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(mContext)
- .inflate(R.layout.split_outline, null);
- mOutlineView = rootLayout.findViewById(R.id.split_outline);
-
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
- FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
- lp.width = mRootBounds.width();
- lp.height = mRootBounds.height();
- lp.token = new Binder();
- lp.setTitle(WINDOW_NAME);
- lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
- // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
- // TRUSTED_OVERLAY for windowless window without input channel.
- mViewHost.setView(rootLayout, lp);
- mLeash = getSurfaceControl(mViewHost.getWindowToken());
-
- drawOutline();
- }
-
- void release() {
- if (mViewHost != null) {
- mViewHost.release();
- mViewHost = null;
- }
- mRootBounds.setEmpty();
- mLastOutlineBounds.setEmpty();
- mOutlineView = null;
- mHostLeash = null;
- mLeash = null;
- }
-
- @Nullable
- SurfaceControl getOutlineLeash() {
- return mLeash;
- }
-
- void setVisibility(boolean visible) {
- if (mOutlineView != null) {
- mOutlineView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- }
- }
-
- void setRootBounds(Rect rootBounds) {
- if (mViewHost == null || mViewHost.getView() == null) {
- return;
- }
-
- if (!mRootBounds.equals(rootBounds)) {
- WindowManager.LayoutParams lp =
- (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
- lp.width = rootBounds.width();
- lp.height = rootBounds.height();
- mViewHost.relayout(lp);
- mRootBounds.set(rootBounds);
- drawOutline();
- }
- }
-
- void onInsetsChanged(InsetsState insetsState) {
- if (!mInsetsState.equals(insetsState)) {
- mInsetsState.set(insetsState);
- drawOutline();
- }
- }
-
- private void computeOutlineBounds(Rect rootBounds, InsetsState insetsState, Rect outBounds) {
- outBounds.set(rootBounds);
- final InsetsSource taskBarInsetsSource =
- insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
- // Only insets the divider bar with task bar when it's expanded so that the rounded corners
- // will be drawn against task bar.
- if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
- outBounds.inset(taskBarInsetsSource.calculateVisibleInsets(outBounds));
- }
-
- // Offset the coordinate from screen based to surface based.
- outBounds.offset(-rootBounds.left, -rootBounds.top);
- }
-
- void drawOutline() {
- if (mOutlineView == null) {
- return;
- }
-
- computeOutlineBounds(mRootBounds, mInsetsState, mTempRect);
- if (mTempRect.equals(mLastOutlineBounds)) {
- return;
- }
-
- ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) mOutlineView.getLayoutParams();
- lp.leftMargin = mTempRect.left;
- lp.topMargin = mTempRect.top;
- lp.width = mTempRect.width();
- lp.height = mTempRect.height();
- mOutlineView.setLayoutParams(lp);
- mLastOutlineBounds.set(mTempRect);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java
deleted file mode 100644
index 94dd9b2..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java
+++ /dev/null
@@ -1,82 +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.wm.shell.splitscreen;
-
-import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
-import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
-import static android.view.RoundedCorner.POSITION_TOP_LEFT;
-import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.util.AttributeSet;
-import android.view.RoundedCorner;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.R;
-
-/** View for drawing split outline. */
-public class OutlineView extends View {
- private final Paint mPaint = new Paint();
- private final Path mPath = new Path();
- private final float[] mRadii = new float[8];
-
- public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeWidth(
- getResources().getDimension(R.dimen.accessibility_focus_highlight_stroke_width));
- mPaint.setColor(getResources().getColor(R.color.system_accent1_100, null));
- }
-
- @Override
- protected void onAttachedToWindow() {
- // TODO(b/200850654): match the screen corners with the actual display decor.
- mRadii[0] = mRadii[1] = getCornerRadius(POSITION_TOP_LEFT);
- mRadii[2] = mRadii[3] = getCornerRadius(POSITION_TOP_RIGHT);
- mRadii[4] = mRadii[5] = getCornerRadius(POSITION_BOTTOM_RIGHT);
- mRadii[6] = mRadii[7] = getCornerRadius(POSITION_BOTTOM_LEFT);
- }
-
- private int getCornerRadius(@RoundedCorner.Position int position) {
- final RoundedCorner roundedCorner = getDisplay().getRoundedCorner(position);
- return roundedCorner == null ? 0 : roundedCorner.getRadius();
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (changed) {
- mPath.reset();
- mPath.addRoundRect(0, 0, getWidth(), getHeight(), mRadii, Path.Direction.CW);
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- canvas.drawPath(mPath, mPaint);
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index dc8fb9f..51104e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -16,20 +16,16 @@
package com.android.wm.shell.splitscreen;
-import android.annotation.CallSuper;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Rect;
-import android.view.InsetsSourceControl;
-import android.view.InsetsState;
-import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.SyncTransactionQueue;
/**
@@ -38,19 +34,15 @@
*
* @see StageCoordinator
*/
-class SideStage extends StageTaskListener implements
- DisplayInsetsController.OnInsetsChangedListener {
+class SideStage extends StageTaskListener {
private static final String TAG = SideStage.class.getSimpleName();
- private final Context mContext;
- private OutlineManager mOutlineManager;
SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession,
+ SurfaceSession surfaceSession, IconProvider iconProvider,
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
- super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
+ super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
stageTaskUnfoldController);
- mContext = context;
}
void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
@@ -83,62 +75,4 @@
wct.reparent(task.token, newParent, false /* onTop */);
return true;
}
-
- @Nullable
- public SurfaceControl getOutlineLeash() {
- return mOutlineManager.getOutlineLeash();
- }
-
- @Override
- @CallSuper
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- super.onTaskAppeared(taskInfo, leash);
- if (isRootTask(taskInfo)) {
- mOutlineManager = new OutlineManager(mContext, taskInfo.configuration);
- enableOutline(true);
- }
- }
-
- @Override
- @CallSuper
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- super.onTaskInfoChanged(taskInfo);
- if (isRootTask(taskInfo)) {
- mOutlineManager.setRootBounds(taskInfo.configuration.windowConfiguration.getBounds());
- }
- }
-
- private boolean isRootTask(ActivityManager.RunningTaskInfo taskInfo) {
- return mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId;
- }
-
- void enableOutline(boolean enable) {
- if (mOutlineManager == null) {
- return;
- }
-
- if (enable) {
- if (mRootTaskInfo != null) {
- mOutlineManager.inflate(mRootLeash,
- mRootTaskInfo.configuration.windowConfiguration.getBounds());
- }
- } else {
- mOutlineManager.release();
- }
- }
-
- void setOutlineVisibility(boolean visible) {
- mOutlineManager.setVisibility(visible);
- }
-
- @Override
- public void insetsChanged(InsetsState insetsState) {
- mOutlineManager.onInsetsChanged(insetsState);
- }
-
- @Override
- public void insetsControlChanged(InsetsState insetsState,
- InsetsSourceControl[] activeControls) {
- insetsChanged(insetsState);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDecorManager.java
new file mode 100644
index 0000000..8e5cc6d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDecorManager.java
@@ -0,0 +1,182 @@
+/*
+ * 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.wm.shell.splitscreen;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Binder;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.SurfaceUtils;
+
+/**
+ * Handles split decor like showing resizing hint for a specific split.
+ */
+class SplitDecorManager extends WindowlessWindowManager {
+ private static final String TAG = SplitDecorManager.class.getSimpleName();
+ private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground";
+
+ private final IconProvider mIconProvider;
+ private final SurfaceSession mSurfaceSession;
+
+ private Drawable mIcon;
+ private ImageView mResizingIconView;
+ private SurfaceControlViewHost mViewHost;
+ private SurfaceControl mHostLeash;
+ private SurfaceControl mIconLeash;
+ private SurfaceControl mBackgroundLeash;
+
+ SplitDecorManager(Configuration configuration, IconProvider iconProvider,
+ SurfaceSession surfaceSession) {
+ super(configuration, null /* rootSurface */, null /* hostInputToken */);
+ mIconProvider = iconProvider;
+ mSurfaceSession = surfaceSession;
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName(TAG)
+ .setHidden(true)
+ .setParent(mHostLeash)
+ .setCallsite("SplitDecorManager#attachToParentSurface");
+ mIconLeash = builder.build();
+ b.setParent(mIconLeash);
+ }
+
+ void inflate(Context context, SurfaceControl rootLeash, Rect rootBounds) {
+ if (mIconLeash != null && mViewHost != null) {
+ return;
+ }
+
+ context = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
+ null /* options */);
+ mHostLeash = rootLeash;
+ mViewHost = new SurfaceControlViewHost(context, context.getDisplay(), this);
+
+ final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(context)
+ .inflate(R.layout.split_decor, null);
+ mResizingIconView = rootLayout.findViewById(R.id.split_resizing_icon);
+
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
+ lp.width = rootBounds.width();
+ lp.height = rootBounds.height();
+ lp.token = new Binder();
+ lp.setTitle(TAG);
+ lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
+ // TRUSTED_OVERLAY for windowless window without input channel.
+ mViewHost.setView(rootLayout, lp);
+ }
+
+ void release(SurfaceControl.Transaction t) {
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+ if (mIconLeash != null) {
+ t.remove(mIconLeash);
+ mIconLeash = null;
+ }
+ if (mBackgroundLeash != null) {
+ t.remove(mBackgroundLeash);
+ mBackgroundLeash = null;
+ }
+ mHostLeash = null;
+ mIcon = null;
+ mResizingIconView = null;
+ }
+
+ /** Showing resizing hint. */
+ void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
+ SurfaceControl.Transaction t) {
+ if (mResizingIconView == null) {
+ return;
+ }
+
+ if (mIcon == null) {
+ // TODO: add fade-in animation.
+ mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
+ RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
+ t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
+ .setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1)
+ .show(mBackgroundLeash);
+
+ mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
+ mResizingIconView.setImageDrawable(mIcon);
+ mResizingIconView.setVisibility(View.VISIBLE);
+
+ WindowManager.LayoutParams lp =
+ (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
+ lp.width = mIcon.getIntrinsicWidth();
+ lp.height = mIcon.getIntrinsicHeight();
+ mViewHost.relayout(lp);
+ t.show(mIconLeash).setLayer(mIconLeash, SPLIT_DIVIDER_LAYER);
+ }
+
+ t.setPosition(mIconLeash,
+ newBounds.width() / 2 - mIcon.getIntrinsicWidth() / 2,
+ newBounds.height() / 2 - mIcon.getIntrinsicWidth() / 2);
+ }
+
+ /** Stops showing resizing hint. */
+ void onResized(Rect newBounds, SurfaceControl.Transaction t) {
+ if (mResizingIconView == null) {
+ return;
+ }
+
+ if (mIcon != null) {
+ mResizingIconView.setVisibility(View.GONE);
+ mResizingIconView.setImageDrawable(null);
+ t.remove(mBackgroundLeash).hide(mIconLeash);
+ mIcon = null;
+ mBackgroundLeash = null;
+ }
+ }
+
+ private static float[] getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
+ final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
+ return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).getComponents();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 3b75bfb..36f1406 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -54,6 +54,7 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
@@ -78,6 +79,7 @@
/**
* Class manages split-screen multitasking mode and implements the main interface
* {@link SplitScreen}.
+ *
* @see StageCoordinator
*/
// TODO(b/198577848): Implement split screen flicker test to consolidate CUJ of split screen.
@@ -96,6 +98,7 @@
private final Transitions mTransitions;
private final TransactionPool mTransactionPool;
private final SplitscreenEventLogger mLogger;
+ private final IconProvider mIconProvider;
private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider;
private StageCoordinator mStageCoordinator;
@@ -105,7 +108,7 @@
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
ShellExecutor mainExecutor, DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- Transitions transitions, TransactionPool transactionPool,
+ Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
@@ -118,6 +121,7 @@
mTransactionPool = transactionPool;
mUnfoldControllerProvider = unfoldControllerProvider;
mLogger = new SplitscreenEventLogger();
+ mIconProvider = iconProvider;
}
public SplitScreen asSplitScreen() {
@@ -140,7 +144,7 @@
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
- mUnfoldControllerProvider);
+ mIconProvider, mUnfoldControllerProvider);
}
}
@@ -165,10 +169,6 @@
return mStageCoordinator.removeFromSideStage(taskId);
}
- public void setSideStageOutline(boolean enable) {
- mStageCoordinator.setSideStageOutline(enable);
- }
-
public void setSideStagePosition(@SplitPosition int sideStagePosition) {
mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */);
}
@@ -319,9 +319,7 @@
}
transaction.apply();
transaction.close();
- return new RemoteAnimationTarget[]{
- mStageCoordinator.getDividerBarLegacyTarget(),
- mStageCoordinator.getOutlineLegacyTarget()};
+ return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 72d9880..3b62afc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -80,6 +80,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
@@ -151,10 +152,12 @@
/** Whether the device is supporting legacy split or not. */
private boolean mUseLegacySplit;
- @SplitScreen.StageType private int mDismissTop = NO_DISMISS;
+ @SplitScreen.StageType
+ private int mDismissTop = NO_DISMISS;
/** The target stage to dismiss to when unlock after folded. */
- @SplitScreen.StageType private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
+ @SplitScreen.StageType
+ private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
private final Runnable mOnTransitionAnimationComplete = () -> {
// If still playing, let it finish.
@@ -169,22 +172,23 @@
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
new SplitWindowManager.ParentContainerCallbacks() {
- @Override
- public void attachToParentSurface(SurfaceControl.Builder b) {
- mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b);
- }
+ @Override
+ public void attachToParentSurface(SurfaceControl.Builder b) {
+ mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b);
+ }
- @Override
- public void onLeashReady(SurfaceControl leash) {
- mSyncQueue.runInSync(t -> applyDividerVisibility(t));
- }
- };
+ @Override
+ public void onLeashReady(SurfaceControl leash) {
+ mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+ }
+ };
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, SplitscreenEventLogger logger,
+ IconProvider iconProvider,
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mContext = context;
mDisplayId = displayId;
@@ -196,11 +200,13 @@
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
mMainStage = new MainStage(
+ mContext,
mTaskOrganizer,
mDisplayId,
mMainStageListener,
mSyncQueue,
mSurfaceSession,
+ iconProvider,
mMainUnfoldController);
mSideStage = new SideStage(
mContext,
@@ -209,10 +215,10 @@
mSideStageListener,
mSyncQueue,
mSurfaceSession,
+ iconProvider,
mSideUnfoldController);
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
- mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage);
mRootTDAOrganizer.registerListener(displayId, this);
final DeviceStateManager deviceStateManager =
mContext.getSystemService(DeviceStateManager.class);
@@ -284,10 +290,6 @@
return result;
}
- void setSideStageOutline(boolean enable) {
- mSideStage.enableOutline(enable);
- }
-
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
@Nullable Bundle sideOptions, @SplitPosition int sidePosition,
@@ -697,9 +699,10 @@
if (bothStageInvisible) {
if (mExitSplitScreenOnHide
- // Don't dismiss staged split when both stages are not visible due to sleeping display,
- // like the cases keyguard showing or screen off.
- || (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) {
+ // Don't dismiss staged split when both stages are not visible due to sleeping
+ // display, like the cases keyguard showing or screen off.
+ || (!mMainStage.mRootTaskInfo.isSleeping
+ && !mSideStage.mRootTaskInfo.isSleeping)) {
exitSplitScreen(null /* childrenToTop */,
SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
}
@@ -719,7 +722,6 @@
t.setVisibility(mSideStage.mRootLeash, bothStageVisible)
.setVisibility(mMainStage.mRootLeash, bothStageVisible);
applyDividerVisibility(t);
- applyOutlineVisibility(t);
}
});
}
@@ -741,19 +743,6 @@
}
}
- private void applyOutlineVisibility(SurfaceControl.Transaction t) {
- final SurfaceControl outlineLeash = mSideStage.getOutlineLeash();
- if (outlineLeash == null) {
- return;
- }
-
- if (mDividerVisible) {
- t.show(outlineLeash).setLayer(outlineLeash, SPLIT_DIVIDER_LAYER);
- } else {
- t.hide(outlineLeash);
- }
- }
-
private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
final boolean hasChildren = stageListener.mHasChildren;
final boolean isSideStage = stageListener == mSideStageListener;
@@ -817,8 +806,11 @@
@Override
public void onLayoutSizeChanging(SplitLayout layout) {
- mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
- mSideStage.setOutlineVisibility(false);
+ mSyncQueue.runInSync(t -> {
+ updateSurfaceBounds(layout, t);
+ mMainStage.onResizing(getMainStageBounds(), t);
+ mSideStage.onResizing(getSideStageBounds(), t);
+ });
}
@Override
@@ -827,8 +819,11 @@
updateWindowBounds(layout, wct);
updateUnfoldBounds();
mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
- mSideStage.setOutlineVisibility(true);
+ mSyncQueue.runInSync(t -> {
+ updateSurfaceBounds(layout, t);
+ mMainStage.onResized(getMainStageBounds(), t);
+ mSideStage.onResized(getSideStageBounds(), t);
+ });
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
}
@@ -893,7 +888,7 @@
if (mSplitLayout == null) {
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
- mDisplayImeController, mTaskOrganizer);
+ mDisplayImeController, mTaskOrganizer, false /* applyDismissingParallax */);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
if (mMainUnfoldController != null && mSideUnfoldController != null) {
@@ -1216,18 +1211,6 @@
null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
}
- RemoteAnimationTarget getOutlineLegacyTarget() {
- final Rect bounds = mSideStage.mRootTaskInfo.configuration.windowConfiguration.getBounds();
- // Leverage TYPE_DOCK_DIVIDER type when wrapping outline remote animation target in order to
- // distinguish as a split auxiliary target in Launcher.
- return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */,
- mSideStage.getOutlineLeash(), false /* isTranslucent */, null /* clipRect */,
- null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */,
- new android.graphics.Point(0, 0) /* position */, bounds, bounds,
- new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */,
- null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
- }
-
@Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 6f1a09d..5100c56 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -26,6 +26,7 @@
import android.annotation.CallSuper;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.SparseArray;
@@ -35,9 +36,11 @@
import androidx.annotation.NonNull;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SurfaceUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.split.SplitDecorManager;
import java.io.PrintWriter;
@@ -72,25 +75,31 @@
void onNoLongerSupportMultiWindow();
}
+ private final Context mContext;
private final StageListenerCallbacks mCallbacks;
private final SurfaceSession mSurfaceSession;
- protected final SyncTransactionQueue mSyncQueue;
+ private final SyncTransactionQueue mSyncQueue;
+ private final IconProvider mIconProvider;
protected ActivityManager.RunningTaskInfo mRootTaskInfo;
protected SurfaceControl mRootLeash;
protected SurfaceControl mDimLayer;
protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>();
private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>();
+ // TODO(b/204308910): Extracts SplitDecorManager related code to common package.
+ private SplitDecorManager mSplitDecorManager;
private final StageTaskUnfoldController mStageTaskUnfoldController;
- StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId,
+ StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession,
+ SurfaceSession surfaceSession, IconProvider iconProvider,
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
+ mContext = context;
mCallbacks = callbacks;
mSyncQueue = syncQueue;
mSurfaceSession = surfaceSession;
+ mIconProvider = iconProvider;
mStageTaskUnfoldController = stageTaskUnfoldController;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
}
@@ -142,13 +151,14 @@
if (mRootTaskInfo == null && !taskInfo.hasParentTask()) {
mRootLeash = leash;
mRootTaskInfo = taskInfo;
+ mSplitDecorManager = new SplitDecorManager(
+ mRootTaskInfo.configuration,
+ mIconProvider,
+ mSurfaceSession);
mCallbacks.onRootTaskAppeared();
sendStatusChanged();
- mSyncQueue.runInSync(t -> {
- t.hide(mRootLeash);
- mDimLayer =
- SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession);
- });
+ mSyncQueue.runInSync(t -> mDimLayer =
+ SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession));
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
final int taskId = taskInfo.taskId;
mChildrenLeashes.put(taskId, leash);
@@ -179,6 +189,17 @@
return;
}
if (mRootTaskInfo.taskId == taskInfo.taskId) {
+ // Inflates split decor view only when the root task is visible.
+ if (mRootTaskInfo.isVisible != taskInfo.isVisible) {
+ mSyncQueue.runInSync(t -> {
+ if (taskInfo.isVisible) {
+ mSplitDecorManager.inflate(mContext, mRootLeash,
+ taskInfo.configuration.windowConfiguration.getBounds());
+ } else {
+ mSplitDecorManager.release(t);
+ }
+ });
+ }
mRootTaskInfo = taskInfo;
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
@@ -205,8 +226,11 @@
final int taskId = taskInfo.taskId;
if (mRootTaskInfo.taskId == taskId) {
mCallbacks.onRootTaskVanished();
- mSyncQueue.runInSync(t -> t.remove(mDimLayer));
mRootTaskInfo = null;
+ mSyncQueue.runInSync(t -> {
+ t.remove(mDimLayer);
+ mSplitDecorManager.release(t);
+ });
} else if (mChildrenTaskInfo.contains(taskId)) {
mChildrenTaskInfo.remove(taskId);
mChildrenLeashes.remove(taskId);
@@ -237,6 +261,18 @@
}
}
+ void onResizing(Rect newBounds, SurfaceControl.Transaction t) {
+ if (mSplitDecorManager != null && mRootTaskInfo != null) {
+ mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, t);
+ }
+ }
+
+ void onResized(Rect newBounds, SurfaceControl.Transaction t) {
+ if (mSplitDecorManager != null) {
+ mSplitDecorManager.onResized(newBounds, t);
+ }
+ }
+
void setBounds(Rect bounds, WindowContainerTransaction wct) {
wct.setBounds(mRootTaskInfo.token, bounds);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
index 574e379..60a6cd7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
@@ -882,7 +882,7 @@
if (mSplitLayout == null) {
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
- mDisplayImeController, mTaskOrganizer);
+ mDisplayImeController, mTaskOrganizer, true /* applyDismissingParallax */);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
if (mMainUnfoldController != null && mSideUnfoldController != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index 38122ff..e2a72bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -314,7 +314,7 @@
@Override
public void stopAnimation() {
- if (mIconAnimator != null) {
+ if (mIconAnimator != null && mIconAnimator.isRunning()) {
mIconAnimator.end();
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index b4caeb5..73eebad 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -68,7 +68,8 @@
mSplitLayoutHandler,
mCallbacks,
mDisplayImeController,
- mTaskOrganizer));
+ mTaskOrganizer,
+ false /* applyDismissingParallax */));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
index 2bcc45e..c972067 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
@@ -25,10 +25,13 @@
import android.view.SurfaceSession;
import android.window.WindowContainerTransaction;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -41,22 +44,24 @@
/** Tests for {@link MainStage} */
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class MainStageTests {
+public class MainStageTests extends ShellTestCase {
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private ActivityManager.RunningTaskInfo mRootTaskInfo;
@Mock private SurfaceControl mRootLeash;
+ @Mock private IconProvider mIconProvider;
private WindowContainerTransaction mWct = new WindowContainerTransaction();
private SurfaceSession mSurfaceSession = new SurfaceSession();
private MainStage mMainStage;
@Before
+ @UiThreadTest
public void setup() {
MockitoAnnotations.initMocks(this);
mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
- mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
- mSurfaceSession, null);
+ mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
+ mSyncQueue, mSurfaceSession, mIconProvider, null);
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
index 838aa81..1857faa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
@@ -33,6 +33,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
@@ -54,6 +55,7 @@
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private ActivityManager.RunningTaskInfo mRootTask;
@Mock private SurfaceControl mRootLeash;
+ @Mock private IconProvider mIconProvider;
@Spy private WindowContainerTransaction mWct;
private SurfaceSession mSurfaceSession = new SurfaceSession();
private SideStage mSideStage;
@@ -64,7 +66,7 @@
MockitoAnnotations.initMocks(this);
mRootTask = new TestRunningTaskInfoBuilder().build();
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession, null);
+ mSyncQueue, mSurfaceSession, mIconProvider, null);
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 05496b0..d5dee82 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -55,6 +55,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
@@ -89,6 +90,7 @@
@Mock private Transitions mTransitions;
@Mock private SurfaceSession mSurfaceSession;
@Mock private SplitscreenEventLogger mLogger;
+ @Mock private IconProvider mIconProvider;
private SplitLayout mSplitLayout;
private MainStage mMainStage;
private SideStage mSideStage;
@@ -107,11 +109,13 @@
doReturn(mockExecutor).when(mTransitions).getAnimExecutor();
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
mSplitLayout = SplitTestUtils.createMockSplitLayout();
- mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
- StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null);
+ mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
+ StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
+ mIconProvider, null);
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
- StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null);
+ StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
+ mIconProvider, null);
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 3ed72e2..53d5076 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -35,10 +35,13 @@
import android.view.SurfaceSession;
import android.window.WindowContainerTransaction;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -57,7 +60,7 @@
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public final class StageTaskListenerTests {
+public final class StageTaskListenerTests extends ShellTestCase {
private static final boolean ENABLE_SHELL_TRANSITIONS =
SystemProperties.getBoolean("persist.debug.shell_transit", false);
@@ -68,6 +71,8 @@
@Mock
private SyncTransactionQueue mSyncQueue;
@Mock
+ private IconProvider mIconProvider;
+ @Mock
private StageTaskUnfoldController mStageTaskUnfoldController;
@Captor
private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
@@ -77,14 +82,17 @@
private StageTaskListener mStageTaskListener;
@Before
+ @UiThreadTest
public void setup() {
MockitoAnnotations.initMocks(this);
mStageTaskListener = new StageTaskListener(
+ mContext,
mTaskOrganizer,
DEFAULT_DISPLAY,
mCallbacks,
mSyncQueue,
mSurfaceSession,
+ mIconProvider,
mStageTaskUnfoldController);
mRootTask = new TestRunningTaskInfoBuilder().build();
mRootTask.parentTaskId = INVALID_TASK_ID;
diff --git a/media/Android.bp b/media/Android.bp
index aa07c13..15b24b2 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -68,13 +68,17 @@
"aidl/android/media/audio/common/AudioGain.aidl",
"aidl/android/media/audio/common/AudioGainConfig.aidl",
"aidl/android/media/audio/common/AudioGainMode.aidl",
+ "aidl/android/media/audio/common/AudioInputFlags.aidl",
+ "aidl/android/media/audio/common/AudioIoFlags.aidl",
"aidl/android/media/audio/common/AudioMMapPolicy.aidl",
"aidl/android/media/audio/common/AudioMMapPolicyInfo.aidl",
"aidl/android/media/audio/common/AudioMMapPolicyType.aidl",
"aidl/android/media/audio/common/AudioMode.aidl",
"aidl/android/media/audio/common/AudioOffloadInfo.aidl",
+ "aidl/android/media/audio/common/AudioOutputFlags.aidl",
"aidl/android/media/audio/common/AudioPort.aidl",
"aidl/android/media/audio/common/AudioPortConfig.aidl",
+ "aidl/android/media/audio/common/AudioPortDeviceExt.aidl",
"aidl/android/media/audio/common/AudioPortExt.aidl",
"aidl/android/media/audio/common/AudioPortMixExt.aidl",
"aidl/android/media/audio/common/AudioPortMixExtUseCase.aidl",
diff --git a/media/aidl/android/media/audio/common/AudioGain.aidl b/media/aidl/android/media/audio/common/AudioGain.aidl
index 90fdeb6..dbfcd8c 100644
--- a/media/aidl/android/media/audio/common/AudioGain.aidl
+++ b/media/aidl/android/media/audio/common/AudioGain.aidl
@@ -43,4 +43,6 @@
int minRampMs;
/** Maximum ramp duration in milliseconds. */
int maxRampMs;
+ /** Indicates whether it is allowed to use this stage for volume control. */
+ boolean useForVolume;
}
diff --git a/media/aidl/android/media/audio/common/AudioInputFlags.aidl b/media/aidl/android/media/audio/common/AudioInputFlags.aidl
new file mode 100644
index 0000000..e4b6ec2
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioInputFlags.aidl
@@ -0,0 +1,62 @@
+/*
+ * 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.audio.common;
+
+/**
+ * Specifies options applicable to audio input. These can be functional
+ * requests or performance requests. These flags apply both to audio ports and
+ * audio streams. Flags specified for an audio stream are usually used to find
+ * the best matching audio port for it.
+ *
+ * {@hide}
+ */
+@VintfStability
+@Backing(type="int")
+enum AudioInputFlags {
+ /**
+ * Input is optimized for decreasing audio latency.
+ */
+ FAST = 0,
+ /**
+ * Input is for capturing "hotword" audio commands.
+ */
+ HW_HOTWORD = 1,
+ /**
+ * Input stream should only have minimal signal processing applied.
+ */
+ RAW = 2,
+ /**
+ * Input stream needs to be synchronized with an output stream.
+ */
+ SYNC = 3,
+ /**
+ * Input uses MMAP no IRQ mode--direct memory mapping with the hardware.
+ */
+ MMAP_NOIRQ = 4,
+ /**
+ * Input is used for receiving VoIP audio.
+ */
+ VOIP_TX = 5,
+ /**
+ * Input stream contains AV synchronization markers embedded.
+ */
+ HW_AV_SYNC = 6,
+ /**
+ * Input contains an encoded audio stream.
+ */
+ DIRECT = 7,
+}
diff --git a/core/java/android/companion/Association.aidl b/media/aidl/android/media/audio/common/AudioIoFlags.aidl
similarity index 60%
copy from core/java/android/companion/Association.aidl
copy to media/aidl/android/media/audio/common/AudioIoFlags.aidl
index 2a28f1f..f978fb6 100644
--- a/core/java/android/companion/Association.aidl
+++ b/media/aidl/android/media/audio/common/AudioIoFlags.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -13,6 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package android.media.audio.common;
+
+/**
+ * Stores a bitmask of input or output flags.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+union AudioIoFlags {
+ /** Bitmask indexed by AudioInputFlags. */
+ int input;
+ /** Bitmask indexed by AudioOutputFlags. */
+ int output;
+}
diff --git a/media/aidl/android/media/audio/common/AudioOutputFlags.aidl b/media/aidl/android/media/audio/common/AudioOutputFlags.aidl
new file mode 100644
index 0000000..0505036
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioOutputFlags.aidl
@@ -0,0 +1,100 @@
+/*
+ * 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.audio.common;
+
+/**
+ * Specifies options applicable to audio output. These can be functional
+ * requests or performance requests. These flags apply both to audio ports and
+ * audio streams. Flags specified for an audio stream are usually used to find
+ * the best matching audio port for it.
+ *
+ * {@hide}
+ */
+@VintfStability
+@Backing(type="int")
+enum AudioOutputFlags {
+ /**
+ * Output must not be altered by the framework, it bypasses software mixers.
+ */
+ DIRECT = 0,
+ /**
+ * When used with audio ports, indicates the "main" (primary) port. This
+ * port is opened by default and receives routing, audio mode and volume
+ * controls related to voice calls.
+ */
+ PRIMARY = 1,
+ /**
+ * Output is optimized for decreasing audio latency.
+ */
+ FAST = 2,
+ /**
+ * Output is optimized for having the low power consumption.
+ */
+ DEEP_BUFFER = 3,
+ /**
+ * Output is compressed audio format, intended for hardware decoding.
+ */
+ COMPRESS_OFFLOAD = 4,
+ /**
+ * Write operations must return as fast as possible instead of
+ * being blocked until all provided data has been consumed.
+ */
+ NON_BLOCKING = 5,
+ /**
+ * Output stream contains AV synchronization markers embedded.
+ */
+ HW_AV_SYNC = 6,
+ /**
+ * Used to support ultrasonic communication with beacons.
+ * Note: "TTS" here means "Transmitted Through Speaker",
+ * not "Text-to-Speech".
+ */
+ TTS = 7,
+ /**
+ * Output stream should only have minimal signal processing applied.
+ */
+ RAW = 8,
+ /**
+ * Output stream needs to be synchronized with an input stream.
+ */
+ SYNC = 9,
+ /**
+ * Output stream is encoded according to IEC958.
+ */
+ IEC958_NONAUDIO = 10,
+ /**
+ * Output must not be altered by the framework and hardware.
+ */
+ DIRECT_PCM = 11,
+ /**
+ * Output uses MMAP no IRQ mode--direct memory mapping with the hardware.
+ */
+ MMAP_NOIRQ = 12,
+ /**
+ * Output is used for transmitting VoIP audio.
+ */
+ VOIP_RX = 13,
+ /**
+ * Output is used for music playback during telephony calls.
+ */
+ INCALL_MUSIC = 14,
+ /**
+ * The rendered must ignore any empty blocks between compressed audio
+ * tracks.
+ */
+ GAPLESS_OFFLOAD = 15,
+}
diff --git a/media/aidl/android/media/audio/common/AudioPort.aidl b/media/aidl/android/media/audio/common/AudioPort.aidl
index 19652bc..8e1c5af 100644
--- a/media/aidl/android/media/audio/common/AudioPort.aidl
+++ b/media/aidl/android/media/audio/common/AudioPort.aidl
@@ -17,6 +17,7 @@
package android.media.audio.common;
import android.media.audio.common.AudioGain;
+import android.media.audio.common.AudioIoFlags;
import android.media.audio.common.AudioPortConfig;
import android.media.audio.common.AudioPortExt;
import android.media.audio.common.AudioProfile;
@@ -45,6 +46,10 @@
*/
AudioProfile[] profiles;
/**
+ * I/O feature flags.
+ */
+ AudioIoFlags flags;
+ /**
* ExtraAudioDescriptors supported by this port. Used for formats not
* recognized by the platform. The audio capability is described by a
* hardware descriptor.
diff --git a/media/aidl/android/media/audio/common/AudioPortConfig.aidl b/media/aidl/android/media/audio/common/AudioPortConfig.aidl
index 44850f4..e3a9374 100644
--- a/media/aidl/android/media/audio/common/AudioPortConfig.aidl
+++ b/media/aidl/android/media/audio/common/AudioPortConfig.aidl
@@ -19,6 +19,7 @@
import android.media.audio.common.AudioChannelLayout;
import android.media.audio.common.AudioFormatDescription;
import android.media.audio.common.AudioGainConfig;
+import android.media.audio.common.AudioIoFlags;
import android.media.audio.common.AudioPortExt;
import android.media.audio.common.Int;
@@ -44,6 +45,8 @@
@nullable AudioFormatDescription format;
/** Gain to apply. Can be left unspecified. */
@nullable AudioGainConfig gain;
+ /** I/O feature flags. Can be left unspecified. */
+ @nullable AudioIoFlags flags;
/** Extra parameters depending on the port role. */
AudioPortExt ext;
}
diff --git a/media/aidl/android/media/audio/common/AudioPortDeviceExt.aidl b/media/aidl/android/media/audio/common/AudioPortDeviceExt.aidl
new file mode 100644
index 0000000..940566c
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioPortDeviceExt.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.audio.common;
+
+import android.media.audio.common.AudioDevice;
+import android.media.audio.common.AudioFormatDescription;
+
+/**
+ * Extra parameters which are specified when the audio port is in the device role.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioPortDeviceExt {
+ /** Audio device specification. */
+ AudioDevice device;
+ /**
+ * List of supported encoded formats. Specified for ports that perform
+ * hardware-accelerated decoding or transcoding, or connected to external
+ * hardware.
+ */
+ AudioFormatDescription[] encodedFormats;
+}
diff --git a/media/aidl/android/media/audio/common/AudioPortExt.aidl b/media/aidl/android/media/audio/common/AudioPortExt.aidl
index 9b60c57..c4681cb 100644
--- a/media/aidl/android/media/audio/common/AudioPortExt.aidl
+++ b/media/aidl/android/media/audio/common/AudioPortExt.aidl
@@ -16,7 +16,7 @@
package android.media.audio.common;
-import android.media.audio.common.AudioDevice;
+import android.media.audio.common.AudioPortDeviceExt;
import android.media.audio.common.AudioPortMixExt;
/**
@@ -30,9 +30,9 @@
union AudioPortExt {
/** Represents an empty union. Value is ignored. */
boolean unspecified;
- /** Audio device specification. */
- AudioDevice device;
- /** Mix specific info. */
+ /** Information specific to device ports. */
+ AudioPortDeviceExt device;
+ /** Information specific to mix ports. */
AudioPortMixExt mix;
/** Audio session identifier. */
int session;
diff --git a/media/aidl/android/media/audio/common/AudioPortMixExt.aidl b/media/aidl/android/media/audio/common/AudioPortMixExt.aidl
index 54c5d8b..f3613a4 100644
--- a/media/aidl/android/media/audio/common/AudioPortMixExt.aidl
+++ b/media/aidl/android/media/audio/common/AudioPortMixExt.aidl
@@ -30,4 +30,16 @@
int handle;
/** Parameters specific to the mix use case. */
AudioPortMixExtUseCase usecase;
+ /**
+ * Maximum number of input or output streams that can be simultaneously
+ * opened for this port.
+ */
+ int maxOpenStreamCount;
+ /**
+ * Maximum number of input or output streams that can be simultaneously
+ * active for this port.
+ */
+ int maxActiveStreamCount;
+ /** Mute duration while changing device, when used for output. */
+ int recommendedMuteDurationMs;
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioGain.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioGain.aidl
index 71891eb..adc5b672 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioGain.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioGain.aidl
@@ -43,4 +43,5 @@
int stepValue;
int minRampMs;
int maxRampMs;
+ boolean useForVolume;
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioInputFlags.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioInputFlags.aidl
new file mode 100644
index 0000000..8a5dae0
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioInputFlags.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioInputFlags {
+ FAST = 0,
+ HW_HOTWORD = 1,
+ RAW = 2,
+ SYNC = 3,
+ MMAP_NOIRQ = 4,
+ VOIP_TX = 5,
+ HW_AV_SYNC = 6,
+ DIRECT = 7,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioIoFlags.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioIoFlags.aidl
new file mode 100644
index 0000000..4a46725
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioIoFlags.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+union AudioIoFlags {
+ int input;
+ int output;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl
new file mode 100644
index 0000000..ed16d17
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioOutputFlags {
+ DIRECT = 0,
+ PRIMARY = 1,
+ FAST = 2,
+ DEEP_BUFFER = 3,
+ COMPRESS_OFFLOAD = 4,
+ NON_BLOCKING = 5,
+ HW_AV_SYNC = 6,
+ TTS = 7,
+ RAW = 8,
+ SYNC = 9,
+ IEC958_NONAUDIO = 10,
+ DIRECT_PCM = 11,
+ MMAP_NOIRQ = 12,
+ VOIP_RX = 13,
+ INCALL_MUSIC = 14,
+ GAPLESS_OFFLOAD = 15,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPort.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPort.aidl
index ceb3774..8f563b3 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPort.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPort.aidl
@@ -38,6 +38,7 @@
int id;
@utf8InCpp String name;
android.media.audio.common.AudioProfile[] profiles;
+ android.media.audio.common.AudioIoFlags flags;
android.media.audio.common.ExtraAudioDescriptor[] extraAudioDescriptors;
android.media.audio.common.AudioGain[] gains;
android.media.audio.common.AudioPortConfig activeConfig;
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortConfig.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortConfig.aidl
index fcc5458..78967b4 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortConfig.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortConfig.aidl
@@ -40,5 +40,6 @@
@nullable android.media.audio.common.AudioChannelLayout channelMask;
@nullable android.media.audio.common.AudioFormatDescription format;
@nullable android.media.audio.common.AudioGainConfig gain;
+ @nullable android.media.audio.common.AudioIoFlags flags;
android.media.audio.common.AudioPortExt ext;
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortDeviceExt.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortDeviceExt.aidl
new file mode 100644
index 0000000..2e8124a
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortDeviceExt.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioPortDeviceExt {
+ android.media.audio.common.AudioDevice device;
+ android.media.audio.common.AudioFormatDescription[] encodedFormats;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortExt.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortExt.aidl
index 241e4e6..af9d9c4 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortExt.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortExt.aidl
@@ -36,7 +36,7 @@
@JavaDerive(equals=true, toString=true) @VintfStability
union AudioPortExt {
boolean unspecified;
- android.media.audio.common.AudioDevice device;
+ android.media.audio.common.AudioPortDeviceExt device;
android.media.audio.common.AudioPortMixExt mix;
int session;
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortMixExt.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortMixExt.aidl
index 547396c..5b74c0d 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortMixExt.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortMixExt.aidl
@@ -37,4 +37,7 @@
parcelable AudioPortMixExt {
int handle;
android.media.audio.common.AudioPortMixExtUseCase usecase;
+ int maxOpenStreamCount;
+ int maxActiveStreamCount;
+ int recommendedMuteDurationMs;
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d061bd3..a6c6fe6 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -96,8 +96,6 @@
private Context mOriginalContext;
private Context mApplicationContext;
private long mVolumeKeyUpTime;
- private boolean mUseFixedVolumeInitialized;
- private boolean mUseFixedVolume;
private static final String TAG = "AudioManager";
private static final boolean DEBUG = false;
private static final AudioPortEventHandler sAudioPortEventHandler = new AudioPortEventHandler();
@@ -893,19 +891,13 @@
* </ul>
*/
public boolean isVolumeFixed() {
- synchronized (this) {
- try {
- if (!mUseFixedVolumeInitialized) {
- mUseFixedVolume = getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_useFixedVolume);
- }
- } catch (Exception e) {
- } finally {
- // only ever try once, so always consider initialized even if query failed
- mUseFixedVolumeInitialized = true;
- }
+ boolean res = false;
+ try {
+ res = getService().isVolumeFixed();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error querying isVolumeFixed", e);
}
- return mUseFixedVolume;
+ return res;
}
/**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 5b05cd3..d736c25 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -454,4 +454,6 @@
void registerSpatializerOutputCallback(in ISpatializerOutputCallback cb);
void unregisterSpatializerOutputCallback(in ISpatializerOutputCallback cb);
+
+ boolean isVolumeFixed();
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 49477b9..374cc75 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -182,10 +182,15 @@
public String mName;
public int mValue;
public boolean mDefault;
+ public boolean mInternal;
public Feature(String name, int value, boolean def) {
+ this(name, value, def, false /* internal */);
+ }
+ public Feature(String name, int value, boolean def, boolean internal) {
mName = name;
mValue = value;
mDefault = def;
+ mInternal = internal;
}
}
@@ -579,6 +584,11 @@
public static final String FEATURE_LowLatency = "low-latency";
/**
+ * Do not include in REGULAR_CODECS list in MediaCodecList.
+ */
+ private static final String FEATURE_SpecialCodec = "special-codec";
+
+ /**
* <b>video encoder only</b>: codec supports quantization parameter bounds.
* @see MediaFormat#KEY_VIDEO_QP_MAX
* @see MediaFormat#KEY_VIDEO_QP_MIN
@@ -616,6 +626,8 @@
new Feature(FEATURE_MultipleFrames, (1 << 5), false),
new Feature(FEATURE_DynamicTimestamp, (1 << 6), false),
new Feature(FEATURE_LowLatency, (1 << 7), true),
+ // feature to exclude codec from REGULAR codec list
+ new Feature(FEATURE_SpecialCodec, (1 << 30), false, true),
};
private static final Feature[] encoderFeatures = {
@@ -623,6 +635,8 @@
new Feature(FEATURE_MultipleFrames, (1 << 1), false),
new Feature(FEATURE_DynamicTimestamp, (1 << 2), false),
new Feature(FEATURE_QpBounds, (1 << 3), false),
+ // feature to exclude codec from REGULAR codec list
+ new Feature(FEATURE_SpecialCodec, (1 << 30), false, true),
};
/** @hide */
@@ -630,7 +644,9 @@
Feature[] features = getValidFeatures();
String[] res = new String[features.length];
for (int i = 0; i < res.length; i++) {
- res[i] = features[i].mName;
+ if (!features[i].mInternal) {
+ res[i] = features[i].mName;
+ }
}
return res;
}
@@ -778,6 +794,10 @@
// check feature support
for (Feature feat: getValidFeatures()) {
+ if (feat.mInternal) {
+ continue;
+ }
+
Integer yesNo = (Integer)map.get(MediaFormat.KEY_FEATURE_ + feat.mName);
if (yesNo == null) {
continue;
@@ -1091,7 +1111,9 @@
mFlagsRequired |= feat.mValue;
}
mFlagsSupported |= feat.mValue;
- mDefaultFormat.setInteger(key, 1);
+ if (!feat.mInternal) {
+ mDefaultFormat.setInteger(key, 1);
+ }
// TODO restrict features by mFlagsVerified once all codecs reliably verify them
}
}
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 08a648a..4891d74 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -754,7 +754,7 @@
* <p>This key is only used during decoding.
*/
public static final String KEY_MAX_OUTPUT_CHANNEL_COUNT =
- "max-output-channel_count";
+ "max-output-channel-count";
/**
* A key describing the number of frames to trim from the start of the decoded audio stream.
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 9dc4949..405f9ee 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -751,8 +751,13 @@
*/
@Result
public int tune(@NonNull FrontendSettings settings) {
- Log.d(TAG, "Tune to " + settings.getFrequency());
- mFrontendType = settings.getType();
+ final int type = settings.getType();
+ if (mFrontendHandle != null && type != mFrontendType) {
+ Log.e(TAG, "Frontend was opened with type " + mFrontendType + ", new type is " + type);
+ return RESULT_INVALID_STATE;
+ }
+ Log.d(TAG, "Tune to " + settings.getFrequencyLong());
+ mFrontendType = type;
if (mFrontendType == FrontendSettings.TYPE_DTMB) {
if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
TunerVersionChecker.TUNER_VERSION_1_1, "Tuner with DTMB Frontend")) {
diff --git a/packages/PackageInstaller/OWNERS b/packages/PackageInstaller/OWNERS
index c633113..2736870 100644
--- a/packages/PackageInstaller/OWNERS
+++ b/packages/PackageInstaller/OWNERS
@@ -1,7 +1,5 @@
svetoslavganov@google.com
-toddke@google.com
-patb@google.com
-suprabh@google.com
+include /PACKAGE_MANAGER_OWNERS
# For automotive related changes
rogerxue@google.com
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index 1d29966..ed12e83 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -68,7 +68,7 @@
}
for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
- if (entry.getValue().equals(BluetoothUuid.BASE_UUID)) {
+ if (entry.getValue().equals(BluetoothUuid.CAP)) {
return entry.getKey();
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 3347920..5e2f310 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -338,7 +338,7 @@
.getGroupUuidMapByDevice(cachedDevice.getDevice());
if (groupIdMap != null) {
for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
- if (entry.getValue().equals(BluetoothUuid.BASE_UUID)) {
+ if (entry.getValue().equals(BluetoothUuid.CAP)) {
cachedDevice.setGroupId(entry.getKey());
break;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 274696b..468aa05 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -183,6 +183,20 @@
return setBadge(badge);
}
+ /**
+ * Sets the managed badge to this user icon if the device has a device owner.
+ */
+ public UserIconDrawable setBadgeIfManagedDevice(Context context) {
+ Drawable badge = null;
+ boolean deviceOwnerExists = context.getSystemService(DevicePolicyManager.class)
+ .getDeviceOwnerComponentOnAnyUser() != null;
+ if (deviceOwnerExists) {
+ badge = getDrawableForDisplayDensity(
+ context, com.android.internal.R.drawable.ic_corp_badge_case);
+ }
+ return setBadge(badge);
+ }
+
public void setBadgeRadius(float radius) {
mBadgeRadius = radius;
onBoundsChange(getBounds());
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS b/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS
index a0e28ba..5b2e1e3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS
@@ -1,5 +1,6 @@
# Default reviewers for this and subdirectories.
takaoka@google.com
yukawa@google.com
+wilsonwu@google.com
# Emergency approvers in case the above are not available
\ No newline at end of file
diff --git a/packages/SettingsProvider/OWNERS b/packages/SettingsProvider/OWNERS
index 6c61d4b9..5ade971 100644
--- a/packages/SettingsProvider/OWNERS
+++ b/packages/SettingsProvider/OWNERS
@@ -2,6 +2,4 @@
hackbod@google.com
narayan@google.com
svetoslavganov@google.com
-schfan@google.com
-toddke@google.com
-patb@google.com
+include /PACKAGE_MANAGER_OWNERS
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 989010e..a16f5cd 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -123,18 +123,18 @@
/** Interface for launching Intents, which can differ on the lockscreen */
interface IntentStarter {
- default void startFromAction(SmartspaceAction action, View v) {
+ default void startFromAction(SmartspaceAction action, View v, boolean showOnLockscreen) {
if (action.getIntent() != null) {
- startIntent(v, action.getIntent());
+ startIntent(v, action.getIntent(), showOnLockscreen);
} else if (action.getPendingIntent() != null) {
- startPendingIntent(action.getPendingIntent());
+ startPendingIntent(action.getPendingIntent(), showOnLockscreen);
}
}
/** Start the intent */
- void startIntent(View v, Intent i);
+ void startIntent(View v, Intent i, boolean showOnLockscreen);
/** Start the PendingIntent */
- void startPendingIntent(PendingIntent pi);
+ void startPendingIntent(PendingIntent pi, boolean showOnLockscreen);
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index 757dc2e..b83ea4a 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -55,13 +55,14 @@
/**
* Asks QS to update its presentation, according to {@code NotificationPanelViewController}.
- *
* @param qsExpansionFraction How much each UI element in QS should be expanded (QQS to QS.)
* @param panelExpansionFraction Whats the expansion of the whole shade.
* @param headerTranslation How much we should vertically translate QS.
+ * @param squishinessFraction Fraction that affects tile height. 0 when collapsed,
+ * 1 when expanded.
*/
void setQsExpansion(float qsExpansionFraction, float panelExpansionFraction,
- float headerTranslation);
+ float headerTranslation, float squishinessFraction);
void setHeaderListening(boolean listening);
void notifyCustomizeChanged();
void setContainerController(QSContainerController controller);
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 6114728..a110413 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -1,20 +1,3 @@
--keep class com.android.systemui.navigationbar.buttons.KeyButtonView {
- public float getDrawingAlpha();
- public void setDrawingAlpha(float);
-}
-
--keep class com.android.systemui.navigationbar.buttons.KeyButtonRipple {
- public float getGlowAlpha();
- public float getGlowScale();
- public void setGlowAlpha(float);
- public void setGlowScale(float);
-}
-
--keep class com.android.systemui.settings.brightness.BrightnessSliderView {
- public float getSliderScaleY();
- public void setSliderScaleY(float);
-}
-
-keep class com.android.systemui.recents.OverviewProxyRecentsImpl
-keep class com.android.systemui.statusbar.car.CarStatusBar
-keep class com.android.systemui.statusbar.phone.StatusBar
diff --git a/packages/SystemUI/res-keyguard/drawable/face_auth_wallpaper.png b/packages/SystemUI/res-keyguard/drawable/face_auth_wallpaper.png
deleted file mode 100644
index b907f4e..0000000
--- a/packages/SystemUI/res-keyguard/drawable/face_auth_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
index d816b3a..17765b5 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
@@ -23,6 +23,6 @@
<dimen name="widget_big_font_size">88dp</dimen>
<dimen name="qs_header_system_icons_area_height">0dp</dimen>
- <dimen name="qs_panel_padding_top">0dp</dimen>
+ <dimen name="qs_panel_padding_top">@dimen/qqs_layout_margin_top</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 6b14c96..10a2f4c 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -63,7 +63,7 @@
android:id="@+id/qqs_footer_actions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
+ android:layout_marginTop="@dimen/qqs_layout_margin_top"
android:layout_marginStart="@dimen/qs_footer_margin"
android:layout_marginEnd="@dimen/qs_footer_margin"
/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2de8b49..fc455be 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2878,7 +2878,7 @@
<!-- Summary for media output group with the active device count [CHAR LIMIT=NONE] -->
<string name="media_output_dialog_multiple_devices"><xliff:g id="count" example="2">%1$d</xliff:g> devices selected</string>
<!-- Summary for disconnected status [CHAR LIMIT=50] -->
- <string name="media_output_dialog_disconnected"><xliff:g id="device_name" example="My device">%1$s</xliff:g> (disconnected)</string>
+ <string name="media_output_dialog_disconnected">(disconnected)</string>
<!-- Summary for connecting error message [CHAR LIMIT=NONE] -->
<string name="media_output_dialog_connect_failed">Couldn\'t connect. Try again.</string>
<!-- Title for pairing item [CHAR LIMIT=60] -->
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
index 5b6845f..3a8ee29 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
@@ -23,11 +23,18 @@
import android.os.Bundle;
import android.util.Log;
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
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;
@@ -43,7 +50,7 @@
* To restore a flag back to its default, leave the `--ez value <0|1>` off of the command.
*/
@SysUISingleton
-public class FeatureFlagManager implements FlagReader, FlagWriter {
+public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
private static final String TAG = "SysUIFlags";
private static final String SYSPROP_PREFIX = "persist.systemui.flag_";
@@ -58,11 +65,13 @@
private final Map<Integer, Boolean> mBooleanFlagCache = new HashMap<>();
@Inject
- public FeatureFlagManager(SystemPropertiesHelper systemPropertiesHelper, Context context) {
+ public FeatureFlagManager(SystemPropertiesHelper systemPropertiesHelper, Context context,
+ DumpManager dumpManager) {
mSystemPropertiesHelper = systemPropertiesHelper;
IntentFilter filter = new IntentFilter(ACTION_SET_FLAG);
context.registerReceiver(mReceiver, filter, FLAGS_PERMISSION, null);
+ dumpManager.registerDumpable(TAG, this);
}
/** Return a {@link BooleanFlag}'s value. */
@@ -186,4 +195,16 @@
}
}
};
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ 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);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index cac90ea..8c7ede2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -49,7 +49,7 @@
public class KeyguardDisplayManager {
protected static final String TAG = "KeyguardDisplayManager";
- private static boolean DEBUG = KeyguardConstants.DEBUG;
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
private MediaRouter mMediaRouter = null;
private final DisplayManager mDisplayService;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
index b7398d8..e2a2d07 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
@@ -22,6 +22,7 @@
import android.graphics.Rect
import android.hardware.biometrics.BiometricOverlayConstants
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
import android.hardware.display.DisplayManager
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
@@ -181,6 +182,7 @@
@BiometricOverlayConstants.ShowReason
private fun Int.isReasonToShow(): Boolean = when (this) {
REASON_AUTH_KEYGUARD -> false
+ REASON_AUTH_SETTINGS -> false
else -> true
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index b2a5409..11addf0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -40,7 +40,7 @@
@Nullable private UdfpsEnrollHelper mEnrollHelper;
@NonNull private List<UdfpsEnrollProgressBarSegment> mSegments = new ArrayList<>();
- private int mTotalSteps = 1;
+ private int mTotalSteps = 0;
private int mProgressSteps = 0;
private boolean mIsShowingHelp = false;
@@ -67,22 +67,19 @@
void onEnrollmentProgress(int remaining, int totalSteps) {
mTotalSteps = totalSteps;
- updateState(getProgressSteps(remaining, totalSteps), false /* isShowingHelp */);
+
+ // Show some progress for the initial touch.
+ updateState(Math.max(1, totalSteps - remaining), false /* isShowingHelp */);
}
void onEnrollmentHelp(int remaining, int totalSteps) {
- updateState(getProgressSteps(remaining, totalSteps), true /* isShowingHelp */);
+ updateState(Math.max(0, totalSteps - remaining), true /* isShowingHelp */);
}
void onLastStepAcquired() {
updateState(mTotalSteps, false /* isShowingHelp */);
}
- private static int getProgressSteps(int remaining, int totalSteps) {
- // Show some progress for the initial touch.
- return Math.max(1, totalSteps - remaining);
- }
-
private void updateState(int progressSteps, boolean isShowingHelp) {
updateProgress(progressSteps);
updateFillColor(isShowingHelp);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 9d0591e..5c3e07f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -659,7 +659,7 @@
@Override
@AnyThread
public void onTrigger(TriggerEvent event) {
- final Sensor sensor = mSensors[mDevicePosture];
+ final Sensor sensor = mSensors[mPosture];
mDozeLog.traceSensor(mPulseReason);
mHandler.post(mWakeLock.wrap(() -> {
if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagManager.java
index 85baed4..78f0b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagManager.java
@@ -16,23 +16,48 @@
package com.android.systemui.flags;
+import android.util.SparseBooleanArray;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import javax.inject.Inject;
/**
* Default implementation of the a Flag manager that returns default values for release builds
+ *
+ * There's a version of this file in src-debug which allows overriding, and has documentation about
+ * how to set flags.
*/
@SysUISingleton
-public class FeatureFlagManager implements FlagReader, FlagWriter {
+public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
+ SparseBooleanArray mAccessedFlags = new SparseBooleanArray();
@Inject
- public FeatureFlagManager() {}
+ public FeatureFlagManager(DumpManager dumpManager) {
+ dumpManager.registerDumpable("SysUIFlags", this);
+ }
public boolean isEnabled(String key, boolean defaultValue) {
return defaultValue;
}
public boolean isEnabled(int key, boolean defaultValue) {
+ mAccessedFlags.append(key, defaultValue);
return defaultValue;
}
public void setEnabled(String key, boolean value) {}
public void setEnabled(int key, boolean value) {}
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ int size = mAccessedFlags.size();
+ for (int i = 0; i < size; i++) {
+ pw.println(" sysui_flag_" + mAccessedFlags.keyAt(i)
+ + ": " + mAccessedFlags.valueAt(i));
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt
deleted file mode 100644
index b4137fa..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard
-
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
-import android.animation.ValueAnimator
-import android.content.res.Resources
-import android.database.ContentObserver
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
-import android.graphics.Color
-import android.graphics.drawable.ColorDrawable
-import android.hardware.biometrics.BiometricSourceType
-import android.os.Handler
-import android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT
-import android.util.MathUtils
-import android.view.View
-import android.view.WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
-import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-import com.android.internal.annotations.VisibleForTesting
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Dumpable
-import com.android.systemui.R
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.NotificationShadeWindowController
-import com.android.systemui.util.settings.GlobalSettings
-import com.android.systemui.util.settings.SystemSettings
-import java.io.FileDescriptor
-import java.io.PrintWriter
-import java.lang.Float.max
-import java.util.concurrent.TimeUnit
-
-val DEFAULT_ANIMATION_DURATION = TimeUnit.SECONDS.toMillis(4)
-val MAX_SCREEN_BRIGHTNESS = 100 // 0..100
-val MAX_SCRIM_OPACTY = 50 // 0..100
-val DEFAULT_USE_FACE_WALLPAPER = false
-
-/**
- * This class is responsible for ramping up the display brightness (and white overlay) in order
- * to mitigate low light conditions when running face auth without an IR camera.
- */
-@SysUISingleton
-open class FaceAuthScreenBrightnessController(
- private val notificationShadeWindowController: NotificationShadeWindowController,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val resources: Resources,
- private val globalSettings: GlobalSettings,
- private val systemSettings: SystemSettings,
- private val mainHandler: Handler,
- private val dumpManager: DumpManager,
- private val enabled: Boolean
-) : Dumpable {
-
- private var userDefinedBrightness: Float = 1f
- @VisibleForTesting
- var useFaceAuthWallpaper = globalSettings
- .getInt("sysui.use_face_auth_wallpaper", if (DEFAULT_USE_FACE_WALLPAPER) 1 else 0) == 1
- private val brightnessAnimationDuration = globalSettings
- .getLong("sysui.face_brightness_anim_duration", DEFAULT_ANIMATION_DURATION)
- private val maxScreenBrightness = globalSettings
- .getInt("sysui.face_max_brightness", MAX_SCREEN_BRIGHTNESS) / 100f
- private val maxScrimOpacity = globalSettings
- .getInt("sysui.face_max_scrim_opacity", MAX_SCRIM_OPACTY) / 100f
- private val keyguardUpdateCallback = object : KeyguardUpdateMonitorCallback() {
- override fun onBiometricRunningStateChanged(
- running: Boolean,
- biometricSourceType: BiometricSourceType?
- ) {
- if (biometricSourceType != BiometricSourceType.FACE) {
- return
- }
- // TODO enable only when receiving a low-light error
- overridingBrightness = if (enabled) running else false
- }
- }
- private lateinit var whiteOverlay: View
- private var brightnessAnimator: ValueAnimator? = null
- private var overridingBrightness = false
- set(value) {
- if (field == value) {
- return
- }
- field = value
- brightnessAnimator?.cancel()
-
- if (!value) {
- notificationShadeWindowController.setFaceAuthDisplayBrightness(BRIGHTNESS_OVERRIDE_NONE)
- if (whiteOverlay.alpha > 0) {
- brightnessAnimator = createAnimator(whiteOverlay.alpha, 0f).apply {
- duration = 200
- addUpdateListener {
- whiteOverlay.alpha = it.animatedValue as Float
- }
- addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- whiteOverlay.visibility = View.INVISIBLE
- brightnessAnimator = null
- }
- })
- start()
- }
- }
- return
- }
-
- val targetBrightness = max(maxScreenBrightness, userDefinedBrightness)
- whiteOverlay.visibility = View.VISIBLE
- brightnessAnimator = createAnimator(0f, 1f).apply {
- duration = brightnessAnimationDuration
- addUpdateListener {
- val progress = it.animatedValue as Float
- val brightnessProgress = MathUtils.constrainedMap(
- userDefinedBrightness, targetBrightness, 0f, 0.5f, progress)
- val scrimProgress = MathUtils.constrainedMap(
- 0f, maxScrimOpacity, 0.5f, 1f, progress)
- notificationShadeWindowController.setFaceAuthDisplayBrightness(brightnessProgress)
- whiteOverlay.alpha = scrimProgress
- }
- addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- brightnessAnimator = null
- }
- })
- start()
- }
- }
-
- @VisibleForTesting
- open fun createAnimator(start: Float, end: Float) = ValueAnimator.ofFloat(start, end)
-
- /**
- * Returns a bitmap that should be used by the lock screen as a wallpaper, if face auth requires
- * a secure wallpaper.
- */
- var faceAuthWallpaper: Bitmap? = null
- get() {
- val user = KeyguardUpdateMonitor.getCurrentUser()
- if (useFaceAuthWallpaper && keyguardUpdateMonitor.isFaceAuthEnabledForUser(user)) {
- val options = BitmapFactory.Options().apply {
- inScaled = false
- }
- return BitmapFactory.decodeResource(resources, R.drawable.face_auth_wallpaper, options)
- }
- return null
- }
- private set
-
- fun attach(overlayView: View) {
- whiteOverlay = overlayView
- whiteOverlay.focusable = FLAG_NOT_FOCUSABLE
- whiteOverlay.background = ColorDrawable(Color.WHITE)
- whiteOverlay.isEnabled = false
- whiteOverlay.alpha = 0f
- whiteOverlay.visibility = View.INVISIBLE
-
- dumpManager.registerDumpable(this.javaClass.name, this)
- keyguardUpdateMonitor.registerCallback(keyguardUpdateCallback)
- systemSettings.registerContentObserver(SCREEN_BRIGHTNESS_FLOAT,
- object : ContentObserver(mainHandler) {
- override fun onChange(selfChange: Boolean) {
- userDefinedBrightness = systemSettings.getFloat(SCREEN_BRIGHTNESS_FLOAT)
- }
- })
- userDefinedBrightness = systemSettings.getFloat(SCREEN_BRIGHTNESS_FLOAT, 1f)
- }
-
- override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
- pw.apply {
- println("overridingBrightness: $overridingBrightness")
- println("useFaceAuthWallpaper: $useFaceAuthWallpaper")
- println("brightnessAnimator: $brightnessAnimator")
- println("brightnessAnimationDuration: $brightnessAnimationDuration")
- println("maxScreenBrightness: $maxScreenBrightness")
- println("userDefinedBrightness: $userDefinedBrightness")
- println("enabled: $enabled")
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 11d4aac..9b0d69b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -20,9 +20,6 @@
import android.app.trust.TrustManager;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.hardware.face.FaceManager;
-import android.os.Handler;
import android.os.PowerManager;
import com.android.internal.widget.LockPatternUtils;
@@ -37,17 +34,14 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
@@ -59,10 +53,7 @@
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.sensors.AsyncSensorManager;
-import com.android.systemui.util.settings.GlobalSettings;
-import com.android.systemui.util.settings.SystemSettings;
-import java.util.Optional;
import java.util.concurrent.Executor;
import dagger.Lazy;
@@ -150,35 +141,4 @@
return new KeyguardLiftController(statusBarStateController, asyncSensorManager,
keyguardUpdateMonitor, dumpManager);
}
-
- @SysUISingleton
- @Provides
- static Optional<FaceAuthScreenBrightnessController> provideFaceAuthScreenBrightnessController(
- Context context,
- NotificationShadeWindowController notificationShadeWindowController,
- @Main Resources resources,
- Handler handler,
- @Nullable FaceManager faceManager,
- PackageManager packageManager,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- GlobalSettings globalSetting,
- SystemSettings systemSettings,
- DumpManager dumpManager) {
- if (faceManager == null || !packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) {
- return Optional.empty();
- }
-
- // Cameras that support "self illumination," via IR for example, don't need low light
- // environment mitigation.
- boolean needsLowLightMitigation = faceManager.getSensorPropertiesInternal().stream()
- .anyMatch((properties) -> !properties.supportsSelfIllumination);
- if (!needsLowLightMitigation) {
- return Optional.empty();
- }
-
- // currently disabled (doesn't ramp up brightness or use scrim) see b/175918367
- return Optional.of(new FaceAuthScreenBrightnessController(
- notificationShadeWindowController, keyguardUpdateMonitor, resources,
- globalSetting, systemSettings, handler, dumpManager, false));
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 125b87b..113ba59 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -16,14 +16,10 @@
package com.android.systemui.media.dialog;
-import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
-
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
-import android.text.SpannableString;
import android.text.TextUtils;
-import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -99,23 +95,6 @@
return mController.getMediaDevices().size();
}
- @Override
- CharSequence getItemTitle(MediaDevice device) {
- if (device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
- && !device.isConnected()) {
- final CharSequence deviceName = device.getName();
- // Append status to title only for the disconnected Bluetooth device.
- final SpannableString spannableTitle = new SpannableString(
- mContext.getString(R.string.media_output_dialog_disconnected, deviceName));
- spannableTitle.setSpan(new ForegroundColorSpan(
- Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorSecondary)),
- deviceName.length(),
- spannableTitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
- return spannableTitle;
- }
- return super.getItemTitle(device);
- }
-
class MediaDeviceViewHolder extends MediaDeviceBaseViewHolder {
MediaDeviceViewHolder(View view) {
@@ -166,6 +145,14 @@
false /* showProgressBar */, false /* showSubtitle */);
initSeekbar(device);
mCurrentActivePosition = position;
+ } else if (
+ device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
+ && !device.isConnected()) {
+ setTwoLineLayout(device, false /* bFocused */,
+ false /* showSeekBar */, false /* showProgressBar */,
+ true /* showSubtitle */);
+ mSubTitleText.setText(R.string.media_output_dialog_disconnected);
+ mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
} else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */);
mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
@@ -175,7 +162,6 @@
@Override
void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
- super.onBind(customizedItem, topMargin, bottomMargin);
if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
mCheckBox.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 1ffc2c4..868193b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -153,9 +153,7 @@
});
}
- void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
- // TODO (b/201718621): clean up method after adjustment
- }
+ abstract void onBind(int customizedItem, boolean topMargin, boolean bottomMargin);
void setSingleLineLayout(CharSequence title, boolean bFocused) {
mTwoLineLayout.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
index 11d76db..a201c07 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
@@ -126,7 +126,6 @@
@Override
void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
- super.onBind(customizedItem, topMargin, bottomMargin);
if (customizedItem == CUSTOMIZED_ITEM_GROUP) {
setTwoLineLayout(mContext.getText(R.string.media_output_dialog_group),
true /* bFocused */, true /* showSeekBar */, false /* showProgressBar */,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
index e3e2367..2ea706c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
@@ -34,6 +34,8 @@
import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
+import androidx.annotation.Keep;
+
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
@@ -184,19 +186,27 @@
}
}
+ /** Gets the glow alpha, used by {@link android.animation.ObjectAnimator} via reflection. */
+ @Keep
public float getGlowAlpha() {
return mGlowAlpha;
}
+ /** Sets the glow alpha, used by {@link android.animation.ObjectAnimator} via reflection. */
+ @Keep
public void setGlowAlpha(float x) {
mGlowAlpha = x;
invalidateSelf();
}
+ /** Gets the glow scale, used by {@link android.animation.ObjectAnimator} via reflection. */
+ @Keep
public float getGlowScale() {
return mGlowScale;
}
+ /** Sets the glow scale, used by {@link android.animation.ObjectAnimator} via reflection. */
+ @Keep
public void setGlowScale(float x) {
mGlowScale = x;
invalidateSelf();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 1a7a306..7e5ff8a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -166,6 +166,11 @@
updateListening();
}
+ @Override
+ public void setSquishinessFraction(float squishinessFraction) {
+ // No-op, paged layouts are not squishy.
+ }
+
private void updateListening() {
for (TileLayout tilePage : mPages) {
tilePage.setListening(tilePage.getParent() != null && mListening);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 90d3448..44d5e21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -316,8 +316,8 @@
if (mQQSTileHeightAnimator == null) {
mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
- quickTileView.getHeight(), tileView.getHeight());
- qqsTileHeight = quickTileView.getHeight();
+ quickTileView.getMeasuredHeight(), tileView.getMeasuredHeight());
+ qqsTileHeight = quickTileView.getMeasuredHeight();
}
mQQSTileHeightAnimator.addView(quickTileView);
@@ -380,7 +380,7 @@
if (mOtherTilesExpandAnimator == null) {
mOtherTilesExpandAnimator =
new HeightExpansionAnimator(
- this, qqsTileHeight, tileView.getHeight());
+ this, qqsTileHeight, tileView.getMeasuredHeight());
}
mOtherTilesExpandAnimator.addView(tileView);
tileView.setClipChildren(true);
@@ -658,7 +658,7 @@
mTranslateWhileExpanding = shouldTranslate;
}
- static class HeightExpansionAnimator {
+ private static class HeightExpansionAnimator {
private final List<View> mViews = new ArrayList<>();
private final ValueAnimator mAnimator;
private final TouchAnimator.Listener mListener;
@@ -673,9 +673,10 @@
int height = (Integer) valueAnimator.getAnimatedValue();
for (int i = 0; i < viewCount; i++) {
View v = mViews.get(i);
- v.setBottom(v.getTop() + height);
if (v instanceof HeightOverrideable) {
((HeightOverrideable) v).setHeightOverride(height);
+ } else {
+ v.setBottom(v.getTop() + height);
}
}
if (t == 0f) {
@@ -713,9 +714,10 @@
final int viewsCount = mViews.size();
for (int i = 0; i < viewsCount; i++) {
View v = mViews.get(i);
- v.setBottom(v.getTop() + v.getMeasuredHeight());
if (v instanceof HeightOverrideable) {
((HeightOverrideable) v).resetOverride();
+ } else {
+ v.setBottom(v.getTop() + v.getMeasuredHeight());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 89bbcf5..eeca239 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -81,6 +81,7 @@
private QSAnimator mQSAnimator;
private HeightListener mPanelView;
+ private QSSquishinessController mQSSquishinessController;
protected QuickStatusBarHeader mHeader;
protected NonInterceptingScrollView mQSPanelScrollView;
private QSDetail mQSDetail;
@@ -90,6 +91,7 @@
private QSFooter mFooter;
private float mLastQSExpansion = -1;
private float mLastPanelFraction;
+ private float mSquishinessFraction = 1;
private boolean mQsDisabled;
private ImageView mQsDragHandler;
@@ -210,6 +212,7 @@
mQSDetail.setQsPanel(mQSPanelController, mHeader, mFooter, mFalsingManager);
mQSAnimator = qsFragmentComponent.getQSAnimator();
+ mQSSquishinessController = qsFragmentComponent.getQSSquishinessController();
mQSCustomizerController = qsFragmentComponent.getQSCustomizerController();
mQSCustomizerController.init();
@@ -231,7 +234,7 @@
boolean sizeChanged = (oldTop - oldBottom) != (top - bottom);
if (sizeChanged) {
setQsExpansion(mLastQSExpansion, mLastPanelFraction,
- mLastHeaderTranslation);
+ mLastHeaderTranslation, mSquishinessFraction);
}
});
mQSPanelController.setUsingHorizontalLayoutChangeListener(
@@ -413,7 +416,8 @@
mQSAnimator.setShowCollapsedOnKeyguard(showCollapsed);
}
if (!showCollapsed && isKeyguardState()) {
- setQsExpansion(mLastQSExpansion, mLastPanelFraction, 0);
+ setQsExpansion(mLastQSExpansion, mLastPanelFraction, 0,
+ mSquishinessFraction);
}
}
}
@@ -494,12 +498,13 @@
updateShowCollapsedOnKeyguard();
}
mFullShadeProgress = progress;
- setQsExpansion(mLastQSExpansion, mLastPanelFraction, mLastHeaderTranslation);
+ setQsExpansion(mLastQSExpansion, mLastPanelFraction, mLastHeaderTranslation,
+ isTransitioningToFullShade ? progress : mSquishinessFraction);
}
@Override
public void setQsExpansion(float expansion, float panelExpansionFraction,
- float proposedTranslation) {
+ float proposedTranslation, float squishinessFraction) {
float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation;
float progress = mTransitioningToFullShade ? mFullShadeProgress : panelExpansionFraction;
setAlphaAnimationProgress(mInSplitShade ? progress : 1);
@@ -517,11 +522,13 @@
if (expansion == mLastQSExpansion
&& mLastKeyguardAndExpanded == onKeyguardAndExpanded
&& mLastViewHeight == currentHeight
- && mLastHeaderTranslation == headerTranslation) {
+ && mLastHeaderTranslation == headerTranslation
+ && mSquishinessFraction == squishinessFraction) {
return;
}
mLastHeaderTranslation = headerTranslation;
mLastPanelFraction = panelExpansionFraction;
+ mSquishinessFraction = squishinessFraction;
mLastQSExpansion = expansion;
mLastKeyguardAndExpanded = onKeyguardAndExpanded;
mLastViewHeight = currentHeight;
@@ -557,6 +564,9 @@
}
updateQsBounds();
+ if (mQSSquishinessController != null) {
+ mQSSquishinessController.setSquishiness(mSquishinessFraction);
+ }
if (mQSAnimator != null) {
mQSAnimator.setPosition(expansion);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 28aa884..2665f3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -736,6 +736,11 @@
void setListening(boolean listening, UiEventLogger uiEventLogger);
/**
+ * Sets a size modifier for the tile. Where 0 means collapsed, and 1 expanded.
+ */
+ void setSquishinessFraction(float squishinessFraction);
+
+ /**
* Sets the minimum number of rows to show
*
* @param minRows the minimum.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
new file mode 100644
index 0000000..6de8370
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
@@ -0,0 +1,64 @@
+package com.android.systemui.qs
+
+import android.view.ViewGroup
+import com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER
+import com.android.systemui.qs.dagger.QSScope
+import com.android.systemui.qs.tileimpl.HeightOverrideable
+import javax.inject.Inject
+import javax.inject.Named
+
+@QSScope
+class QSSquishinessController @Inject constructor(
+ private val qsTileHost: QSTileHost,
+ @Named(QQS_FOOTER) private val qqsFooterActionsView: FooterActionsView,
+ private val qsAnimator: QSAnimator,
+ private val quickQSPanelController: QuickQSPanelController
+) {
+
+ /**
+ * Fraction from 0 to 1, where 0 is collapsed and 1 expanded.
+ */
+ var squishiness: Float = 1f
+ set(value) {
+ if (field == value) {
+ return
+ }
+ if ((field != 1f && value == 1f) || (field != 0f && value == 0f)) {
+ qsAnimator.requestAnimatorUpdate()
+ }
+ field = value
+ updateSquishiness()
+ }
+
+ /**
+ * Change the height of all tiles and repositions their siblings.
+ */
+ private fun updateSquishiness() {
+ // Start by updating the height of all tiles
+ for (tile in qsTileHost.tiles) {
+ val tileView = quickQSPanelController.getTileView(tile)
+ (tileView as? HeightOverrideable)?.let {
+ it.squishinessFraction = squishiness
+ }
+ }
+
+ // Update tile positions in the layout
+ val tileLayout = quickQSPanelController.tileLayout as TileLayout
+ tileLayout.setSquishinessFraction(squishiness)
+
+ // Calculate how much we should move the footer
+ val tileHeightOffset = tileLayout.height - tileLayout.tilesHeight
+ val footerTopMargin = (qqsFooterActionsView.layoutParams as ViewGroup.MarginLayoutParams)
+ .topMargin
+ val nextTop = tileLayout.bottom - tileHeightOffset + footerTopMargin
+ val amountMoved = nextTop - qqsFooterActionsView.top
+
+ // Move the footer and other siblings (MediaPlayer)
+ (qqsFooterActionsView.parent as ViewGroup?)?.let { parent ->
+ val index = parent.indexOfChild(qqsFooterActionsView)
+ for (i in index until parent.childCount) {
+ parent.getChildAt(i).top += amountMoved
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 1a890a7..ee5d5ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -41,6 +41,8 @@
private int mMinRows = 1;
private int mMaxColumns = NO_MAX_COLUMNS;
protected int mResourceColumns;
+ private float mSquishinessFraction = 1f;
+ private int mLastTileBottom;
public TileLayout(Context context) {
this(context, null);
@@ -210,10 +212,11 @@
return mMaxCellHeight;
}
- protected void layoutTileRecords(int numRecords) {
+ private void layoutTileRecords(int numRecords) {
final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
int row = 0;
int column = 0;
+ mLastTileBottom = 0;
// Layout each QS tile.
final int tilesToLayout = Math.min(numRecords, mRows * mColumns);
@@ -228,7 +231,9 @@
final int top = getRowTop(row);
final int left = getColumnStart(isRtl ? mColumns - column - 1 : column);
final int right = left + mCellWidth;
- record.tileView.layout(left, top, right, top + record.tileView.getMeasuredHeight());
+ final int bottom = top + record.tileView.getMeasuredHeight();
+ record.tileView.layout(left, top, right, bottom);
+ mLastTileBottom = bottom;
}
}
@@ -238,7 +243,7 @@
}
protected int getRowTop(int row) {
- return row * (mCellHeight + mCellMarginVertical);
+ return (int) (row * (mCellHeight * mSquishinessFraction + mCellMarginVertical));
}
protected int getColumnStart(int column) {
@@ -264,4 +269,17 @@
// up.
return Math.max(mColumns * mRows, 1);
}
+
+ public int getTilesHeight() {
+ return mLastTileBottom + getPaddingBottom();
+ }
+
+ @Override
+ public void setSquishinessFraction(float squishinessFraction) {
+ if (Float.compare(mSquishinessFraction, squishinessFraction) == 0) {
+ return;
+ }
+ mSquishinessFraction = squishinessFraction;
+ layoutTileRecords(mRecords.size());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
index 8cc0502..63cbc21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
@@ -21,6 +21,7 @@
import com.android.systemui.qs.QSFooter;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.qs.QSSquishinessController;
import com.android.systemui.qs.QuickQSPanelController;
import com.android.systemui.qs.customize.QSCustomizerController;
@@ -57,4 +58,7 @@
/** Construct a {@link QSCustomizerController}. */
QSCustomizerController getQSCustomizerController();
+
+ /** Construct a {@link QSSquishinessController}. */
+ QSSquishinessController getQSSquishinessController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
index 3b85e5d..210ee93 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
@@ -55,6 +55,8 @@
internal const val DISMISSED = StatusBarManager.TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED
}
+ private var dialogCanceller: ((String) -> Unit)? = null
+
private val commandQueueCallback = object : CommandQueue.Callbacks {
override fun requestAddTile(
componentName: ComponentName,
@@ -67,6 +69,10 @@
callback.onTileRequest(it)
}
}
+
+ override fun cancelRequestAddTile(packageName: String) {
+ dialogCanceller?.invoke(packageName)
+ }
}
fun init() {
@@ -95,16 +101,21 @@
callback.accept(TILE_ALREADY_ADDED)
return
}
- val dialogResponse = object : Consumer<Int> {
- override fun accept(response: Int) {
- if (response == ADD_TILE) {
- addTile(componentName)
- }
- callback.accept(response)
+ val dialogResponse = Consumer<Int> { response ->
+ if (response == ADD_TILE) {
+ addTile(componentName)
}
+ callback.accept(response)
}
val tileData = TileRequestDialog.TileData(appName, label, icon)
- createDialog(tileData, dialogResponse).show()
+ createDialog(tileData, dialogResponse).also { dialog ->
+ dialogCanceller = {
+ if (componentName.packageName == it) {
+ dialog.cancel()
+ }
+ dialogCanceller = null
+ }
+ }.show()
}
private fun createDialog(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/HeightOverrideable.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/HeightOverrideable.kt
index 866fa09..61d68ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/HeightOverrideable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/HeightOverrideable.kt
@@ -21,9 +21,8 @@
const val NO_OVERRIDE = -1
}
- var heightOverride: Int
+ abstract var heightOverride: Int
+ abstract fun resetOverride()
- fun resetOverride() {
- heightOverride = NO_OVERRIDE
- }
+ abstract var squishinessFraction: Float
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index ee5e4df..67c311e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -68,6 +68,18 @@
}
override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
+ set(value) {
+ if (field == value) return
+ field = value
+ updateHeight()
+ }
+
+ override var squishinessFraction: Float = 1f
+ set(value) {
+ if (field == value) return
+ field = value
+ updateHeight()
+ }
private val colorActive = Utils.getColorAttrDefaultColor(context,
com.android.internal.R.attr.colorAccentPrimary)
@@ -148,6 +160,11 @@
updateResources()
}
+ override fun resetOverride() {
+ heightOverride = HeightOverrideable.NO_OVERRIDE
+ updateHeight()
+ }
+
fun updateResources() {
FontSizeUtils.updateFontSize(label, R.dimen.qs_tile_text_size)
FontSizeUtils.updateFontSize(secondaryLabel, R.dimen.qs_tile_text_size)
@@ -218,9 +235,17 @@
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
super.onLayout(changed, l, t, r, b)
- if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
- bottom = top + heightOverride
- }
+ updateHeight()
+ }
+
+ private fun updateHeight() {
+ val actualHeight = (if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
+ heightOverride
+ } else {
+ measuredHeight
+ } * squishinessFraction).toInt()
+ bottom = top + actualHeight
+ scrollY = (actualHeight - height) / 2
}
override fun updateAccessibilityOrder(previousView: View?): View {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index bc21b2d..80ec0ad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -112,20 +112,9 @@
}
mUiHandler.post {
- if (keyguardStateController.isUnlocked) {
- mActivityStarter.startActivity(
- intent, true /* dismissShade */, animationController)
- } else {
- if (state.state == Tile.STATE_ACTIVE) {
- mHost.collapsePanels()
- // With an active tile, don't use ActivityStarter so that the activity is
- // started without prompting keyguard unlock.
- mContext.startActivity(intent)
- } else {
- mActivityStarter.postStartActivityDismissingKeyguard(
- intent, 0 /* delay */, animationController)
- }
- }
+ val showOverLockscreenWhenLocked = state.state == Tile.STATE_ACTIVE
+ mActivityStarter.startActivity(
+ intent, true /* dismissShade */, animationController, showOverLockscreenWhenLocked)
}
}
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 15b78e7..58e8992 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
@@ -331,7 +331,7 @@
showProgressBar();
final boolean isDeviceLocked = mInternetDialogController.isDeviceLocked();
final boolean isWifiEnabled = mWifiManager.isWifiEnabled();
- final boolean isWifiScanEnabled = mWifiManager.isScanAlwaysAvailable();
+ final boolean isWifiScanEnabled = mInternetDialogController.isWifiScanEnabled();
updateWifiToggle(isWifiEnabled, isDeviceLocked);
updateConnectedWifi(isWifiEnabled, isDeviceLocked);
updateWifiScanNotify(isWifiEnabled, isWifiScanEnabled, isDeviceLocked);
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 1ade5ce..40590a7 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
@@ -78,6 +78,7 @@
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.toast.SystemUIToast;
import com.android.systemui.toast.ToastFactory;
import com.android.systemui.util.CarrierConfigTracker;
@@ -150,6 +151,7 @@
private WindowManager mWindowManager;
private ToastFactory mToastFactory;
private SignalDrawable mSignalDrawable;
+ private LocationController mLocationController;
@VisibleForTesting
static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
@@ -199,7 +201,8 @@
GlobalSettings globalSettings, KeyguardStateController keyguardStateController,
WindowManager windowManager, ToastFactory toastFactory,
@Background Handler workerHandler,
- CarrierConfigTracker carrierConfigTracker) {
+ CarrierConfigTracker carrierConfigTracker,
+ LocationController locationController) {
if (DEBUG) {
Log.d(TAG, "Init InternetDialogController");
}
@@ -227,6 +230,7 @@
mWindowManager = windowManager;
mToastFactory = toastFactory;
mSignalDrawable = new SignalDrawable(mContext);
+ mLocationController = locationController;
}
void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
@@ -788,6 +792,14 @@
return false;
}
+ @WorkerThread
+ boolean isWifiScanEnabled() {
+ if (!mLocationController.isLocationEnabled()) {
+ return false;
+ }
+ return mWifiManager.isScanAlwaysAvailable();
+ }
+
static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
final ActivityStarter mActivityStarter;
final WifiEntry mWifiEntry;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index 15aa2b7..a4e1537 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -27,6 +27,7 @@
import android.widget.FrameLayout;
import android.widget.SeekBar.OnSeekBarChangeListener;
+import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -183,6 +184,7 @@
*
* Used in {@link com.android.systemui.qs.QSAnimator}.
*/
+ @Keep
public void setSliderScaleY(float scale) {
if (scale != mScale) {
mScale = scale;
@@ -199,6 +201,7 @@
}
}
+ @Keep
public float getSliderScaleY() {
return mScale;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index f95dd34..c25bc84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -48,6 +48,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -152,6 +153,7 @@
private static final int MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED = 59 << MSG_SHIFT;
private static final int MSG_SET_UDFPS_HBM_LISTENER = 60 << MSG_SHIFT;
private static final int MSG_TILE_SERVICE_REQUEST_ADD = 61 << MSG_SHIFT;
+ private static final int MSG_TILE_SERVICE_REQUEST_CANCEL = 62 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -416,6 +418,11 @@
@NonNull CharSequence label,
@NonNull Icon icon,
@NonNull IAddTileResultCallback callback) {}
+
+ /**
+ * @see IStatusBar#cancelRequestAddTile
+ */
+ default void cancelRequestAddTile(@NonNull String packageName) {}
}
public CommandQueue(Context context) {
@@ -1138,6 +1145,11 @@
mHandler.obtainMessage(MSG_TILE_SERVICE_REQUEST_ADD, args).sendToTarget();
}
+ @Override
+ public void cancelRequestAddTile(@NonNull String s) throws RemoteException {
+ mHandler.obtainMessage(MSG_TILE_SERVICE_REQUEST_CANCEL, s).sendToTarget();
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -1524,6 +1536,11 @@
}
args.recycle();
break;
+ case MSG_TILE_SERVICE_REQUEST_CANCEL:
+ String packageName = (String) msg.obj;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).cancelRequestAddTile(packageName);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index bacb85a..cf9daf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -34,6 +34,7 @@
import android.view.ViewGroup
import com.android.settingslib.Utils
import com.android.systemui.R
+import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
@@ -188,13 +189,28 @@
val ssView = plugin.getView(parent)
ssView.registerDataProvider(plugin)
+
+ val animationController = ActivityLaunchAnimator.Controller.fromView(
+ ssView as View,
+ null /* cujType */
+ )
+
ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
- override fun startIntent(v: View?, i: Intent?) {
- activityStarter.startActivity(i, true /* dismissShade */)
+ override fun startIntent(v: View?, i: Intent?, showOnLockscreen: Boolean) {
+ activityStarter.startActivity(
+ i,
+ true, /* dismissShade */
+ animationController,
+ showOnLockscreen
+ )
}
- override fun startPendingIntent(pi: PendingIntent?) {
- activityStarter.startPendingIntentDismissingKeyguard(pi)
+ override fun startPendingIntent(pi: PendingIntent?, showOnLockscreen: Boolean) {
+ if (showOnLockscreen) {
+ pi?.send()
+ } else {
+ activityStarter.startPendingIntentDismissingKeyguard(pi)
+ }
}
})
ssView.setFalsingManager(falsingManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
index bac5223..e8f352f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
@@ -15,7 +15,7 @@
fun logGutsOpened(key: String, guts: NotificationGuts) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = key
- str2 = guts::class.simpleName
+ str2 = guts.gutsContent::class.simpleName
bool1 = guts.isLeavebehind
}, {
"Guts of type $str2 (leave behind: $bool1) opened for class $str1"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 18f3b45..cd6f35b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -61,7 +61,9 @@
ConversationCoordinator conversationCoordinator,
PreparationCoordinator preparationCoordinator,
MediaCoordinator mediaCoordinator,
+ ShadeEventCoordinator shadeEventCoordinator,
SmartspaceDedupingCoordinator smartspaceDedupingCoordinator,
+ ViewConfigCoordinator viewConfigCoordinator,
VisualStabilityCoordinator visualStabilityCoordinator,
CommunalCoordinator communalCoordinator) {
dumpManager.registerDumpable(TAG, this);
@@ -75,6 +77,8 @@
mCoordinators.add(bubbleCoordinator);
mCoordinators.add(conversationCoordinator);
mCoordinators.add(mediaCoordinator);
+ mCoordinators.add(shadeEventCoordinator);
+ mCoordinators.add(viewConfigCoordinator);
mCoordinators.add(visualStabilityCoordinator);
mCoordinators.add(communalCoordinator);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt
new file mode 100644
index 0000000..f9648a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.service.notification.NotificationListenerService
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.ListEntry
+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
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource
+import javax.inject.Inject
+
+/**
+ * A coordinator which provides callbacks to a view surfaces for various events relevant to the
+ * shade, such as when the user removes a notification, or when the shade is emptied.
+ */
+@SysUISingleton
+class ShadeEventCoordinator @Inject internal constructor(
+ private val mLogger: ShadeEventCoordinatorLogger
+) : Coordinator, NotifShadeEventSource {
+ private var mNotifRemovedByUserCallback: Runnable? = null
+ private var mShadeEmptiedCallback: Runnable? = null
+ private var mEntryRemoved = false
+ private var mEntryRemovedByUser = false
+
+ override fun attach(pipeline: NotifPipeline) {
+ pipeline.addCollectionListener(mNotifCollectionListener)
+ pipeline.addOnBeforeRenderListListener(this::onBeforeRenderList)
+ }
+
+ private val mNotifCollectionListener = object : NotifCollectionListener {
+ override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+ mEntryRemoved = true
+ mEntryRemovedByUser =
+ reason == NotificationListenerService.REASON_CLICK ||
+ reason == NotificationListenerService.REASON_CANCEL_ALL ||
+ reason == NotificationListenerService.REASON_CANCEL
+ }
+ }
+
+ override fun setNotifRemovedByUserCallback(callback: Runnable) {
+ check(mNotifRemovedByUserCallback == null) { "mNotifRemovedByUserCallback already set" }
+ mNotifRemovedByUserCallback = callback
+ }
+
+ override fun setShadeEmptiedCallback(callback: Runnable) {
+ check(mShadeEmptiedCallback == null) { "mShadeEmptiedCallback already set" }
+ mShadeEmptiedCallback = callback
+ }
+
+ private fun onBeforeRenderList(entries: List<ListEntry>) {
+ if (mEntryRemoved && entries.isEmpty()) {
+ mLogger.logShadeEmptied()
+ mShadeEmptiedCallback?.run()
+ }
+ if (mEntryRemoved && mEntryRemovedByUser) {
+ mLogger.logNotifRemovedByUser()
+ mNotifRemovedByUserCallback?.run()
+ }
+ mEntryRemoved = false
+ mEntryRemovedByUser = false
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt
new file mode 100644
index 0000000..c687e1b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+private const val TAG = "ShadeEventCoordinator"
+
+/** Logger for the [ShadeEventCoordinator] */
+class ShadeEventCoordinatorLogger @Inject constructor(
+ @NotificationLog private val buffer: LogBuffer
+) {
+
+ fun logShadeEmptied() {
+ buffer.log(TAG, LogLevel.DEBUG, { }, { "Shade emptied" })
+ }
+
+ fun logNotifRemovedByUser() {
+ buffer.log(TAG, LogLevel.DEBUG, { }, { "Notification removed by user" })
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
new file mode 100644
index 0000000..df1132b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.internal.widget.MessagingGroup
+import com.android.internal.widget.MessagingMessage
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
+import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager
+import com.android.systemui.statusbar.policy.ConfigurationController
+import javax.inject.Inject
+
+/**
+ * A coordinator which ensures that notifications within the new pipeline are correctly inflated
+ * for the current uiMode and screen properties; additionally deferring those changes when a user
+ * change is in progress until that process has completed.
+ */
+@SysUISingleton
+class ViewConfigCoordinator @Inject internal constructor(
+ configurationController: ConfigurationController,
+ lockscreenUserManager: NotificationLockscreenUserManagerImpl,
+ featureFlags: FeatureFlags,
+ private val mGutsManager: NotificationGutsManager,
+ private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor
+) : Coordinator, UserChangedListener, ConfigurationController.ConfigurationListener {
+
+ private var mReinflateNotificationsOnUserSwitched = false
+ private var mDispatchUiModeChangeOnUserSwitched = false
+ private var mPipeline: NotifPipeline? = null
+
+ init {
+ if (featureFlags.isNewNotifPipelineRenderingEnabled) {
+ lockscreenUserManager.addUserChangedListener(this)
+ configurationController.addCallback(this)
+ }
+ }
+
+ override fun attach(pipeline: NotifPipeline) {
+ mPipeline = pipeline
+ }
+
+ override fun onDensityOrFontScaleChanged() {
+ MessagingMessage.dropCache()
+ MessagingGroup.dropCache()
+ if (!mKeyguardUpdateMonitor.isSwitchingUser) {
+ updateNotificationsOnDensityOrFontScaleChanged()
+ } else {
+ mReinflateNotificationsOnUserSwitched = true
+ }
+ }
+
+ override fun onUiModeChanged() {
+ if (!mKeyguardUpdateMonitor.isSwitchingUser) {
+ updateNotificationsOnUiModeChanged()
+ } else {
+ mDispatchUiModeChangeOnUserSwitched = true
+ }
+ }
+
+ override fun onThemeChanged() {
+ onDensityOrFontScaleChanged()
+ }
+
+ override fun onUserChanged(userId: Int) {
+ if (mReinflateNotificationsOnUserSwitched) {
+ updateNotificationsOnDensityOrFontScaleChanged()
+ mReinflateNotificationsOnUserSwitched = false
+ }
+ if (mDispatchUiModeChangeOnUserSwitched) {
+ updateNotificationsOnUiModeChanged()
+ mDispatchUiModeChangeOnUserSwitched = false
+ }
+ }
+
+ private fun updateNotificationsOnUiModeChanged() {
+ mPipeline?.allNotifs?.forEach { entry ->
+ val row = entry.row
+ row?.onUiModeChanged()
+ }
+ }
+
+ private fun updateNotificationsOnDensityOrFontScaleChanged() {
+ mPipeline?.allNotifs?.forEach { entry ->
+ entry.onDensityOrFontScaleChanged()
+ val exposedGuts = entry.areGutsExposed()
+ if (exposedGuts) {
+ mGutsManager.onDensityOrFontScaleChanged(entry)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
new file mode 100644
index 0000000..4ee08ed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 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 static com.android.systemui.statusbar.phone.StatusBar.SPEW;
+
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.statusbar.NotificationVisibility;
+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.render.NotifShadeEventSource;
+
+import org.jetbrains.annotations.NotNull;
+
+import javax.inject.Inject;
+
+/**
+ * This is some logic extracted from the
+ * {@link com.android.systemui.statusbar.phone.StatusBarNotificationPresenter}
+ * into a class that implements a new-pipeline interface so that the new pipeline can implement it
+ * correctly.
+ *
+ * Specifically, this is the logic which updates notifications when uiMode and screen properties
+ * change, and which closes the shade when the last notification disappears.
+ */
+public class LegacyNotificationPresenterExtensions implements NotifShadeEventSource {
+ private static final String TAG = "LegacyNotifPresenter";
+ private final NotificationEntryManager mEntryManager;
+ private boolean mEntryListenerAdded;
+ private Runnable mShadeEmptiedCallback;
+ private Runnable mNotifRemovedByUserCallback;
+
+ @Inject
+ public LegacyNotificationPresenterExtensions(NotificationEntryManager entryManager) {
+ mEntryManager = entryManager;
+ }
+
+ private void ensureEntryListenerAdded() {
+ if (mEntryListenerAdded) return;
+ mEntryListenerAdded = true;
+ mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+ @Override
+ public void onEntryRemoved(
+ @NotNull NotificationEntry entry,
+ NotificationVisibility visibility,
+ boolean removedByUser,
+ int reason) {
+ StatusBarNotification old = entry.getSbn();
+ if (SPEW) {
+ Log.d(TAG, "removeNotification key=" + entry.getKey()
+ + " old=" + old + " reason=" + reason);
+ }
+
+ if (old != null && !mEntryManager.hasActiveNotifications()) {
+ if (mShadeEmptiedCallback != null) mShadeEmptiedCallback.run();
+ }
+ if (removedByUser) {
+ if (mNotifRemovedByUserCallback != null) mNotifRemovedByUserCallback.run();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void setNotifRemovedByUserCallback(@NonNull Runnable callback) {
+ if (mNotifRemovedByUserCallback != null) {
+ throw new IllegalStateException("mNotifRemovedByUserCallback already set");
+ }
+ mNotifRemovedByUserCallback = callback;
+ ensureEntryListenerAdded();
+ }
+
+ @Override
+ public void setShadeEmptiedCallback(@NonNull Runnable callback) {
+ if (mShadeEmptiedCallback != null) {
+ throw new IllegalStateException("mShadeEmptiedCallback already set");
+ }
+ mShadeEmptiedCallback = callback;
+ ensureEntryListenerAdded();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifShadeEventSource.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifShadeEventSource.kt
new file mode 100644
index 0000000..e24f6a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifShadeEventSource.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.render
+
+/**
+ * This is an object which provides callbacks for certain important events related to the
+ * notification shade, such as notifications being removed by the user, or the shade becoming empty.
+ */
+interface NotifShadeEventSource {
+ /**
+ * Registers a callback to be invoked when the last notification has been removed from
+ * the shade for any reason
+ */
+ fun setShadeEmptiedCallback(callback: Runnable)
+
+ /**
+ * Registers a callback to be invoked when a notification has been removed from
+ * the shade by a user action
+ */
+ fun setNotifRemovedByUserCallback(callback: Runnable)
+}
\ 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 dfa1f5f..540216c 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
@@ -45,10 +45,12 @@
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
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;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
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.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.OnUserInteractionCallbackImplLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -59,6 +61,7 @@
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
@@ -90,7 +93,7 @@
/**
* Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
*/
-@Module(includes = { NotificationSectionHeadersModule.class })
+@Module(includes = {NotificationSectionHeadersModule.class})
public interface NotificationsModule {
@Binds
StackScrollAlgorithm.SectionProvider bindSectionProvider(
@@ -271,6 +274,20 @@
}
/**
+ * Provide the active implementation for presenting notifications.
+ */
+ @Provides
+ @SysUISingleton
+ static NotifShadeEventSource provideNotifShadeEventSource(
+ FeatureFlags featureFlags,
+ Lazy<ShadeEventCoordinator> shadeEventCoordinatorLazy,
+ Lazy<LegacyNotificationPresenterExtensions> legacyNotificationPresenterExtensionsLazy) {
+ return featureFlags.isNewNotifPipelineRenderingEnabled()
+ ? shadeEventCoordinatorLazy.get()
+ : legacyNotificationPresenterExtensionsLazy.get();
+ }
+
+ /**
* Provide a dismissal callback that's triggered when a user manually dismissed a notification
* from the notification shade or it gets auto-cancelled by click.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index e956046..02b1210 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1249,6 +1249,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
if (mMenuRow != null && mMenuRow.getMenuView() != null) {
mMenuRow.onConfigurationChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index c5a1f48..4795f08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -653,6 +653,10 @@
return 0f;
}
+ public float getNotificationSquishinessFraction() {
+ return mStackScrollAlgorithm.getNotificationSquishinessFraction(mAmbientState);
+ }
+
void reinflateViews() {
inflateFooterView();
inflateEmptyShadeView();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 09ab90e..94720e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1078,6 +1078,10 @@
mView.setOnStackYChanged(onStackYChanged);
}
+ public float getNotificationSquishinessFraction() {
+ return mView.getNotificationSquishinessFraction();
+ }
+
public float calculateAppearFractionBypass() {
return mView.calculateAppearFractionBypass();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index afe0bba..015edb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -110,6 +110,15 @@
getNotificationChildrenStates(algorithmState, ambientState);
}
+ /**
+ * How expanded or collapsed notifications are when pulling down the shade.
+ * @param ambientState Current ambient state.
+ * @return 0 when fully collapsed, 1 when expanded.
+ */
+ public float getNotificationSquishinessFraction(AmbientState ambientState) {
+ return getExpansionFractionWithoutShelf(mTempAlgorithmState, ambientState);
+ }
+
private void resetChildViewStates() {
int numChildren = mHostView.getChildCount();
for (int i = 0; i < numChildren; i++) {
@@ -364,7 +373,6 @@
final float stackHeight = ambientState.getStackHeight() - shelfHeight - scrimPadding;
final float stackEndHeight = ambientState.getStackEndHeight() - shelfHeight - scrimPadding;
-
return stackHeight / stackEndHeight;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index c639eec..e26f75f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -1078,10 +1078,14 @@
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(ControlsUiController.EXTRA_ANIMATE, true);
+ ActivityLaunchAnimator.Controller controller =
+ v != null ? ActivityLaunchAnimator.Controller.fromView(v, null /* cujType */)
+ : null;
if (mControlsComponent.getVisibility() == AVAILABLE) {
- mContext.startActivity(intent);
+ mActivityStarter.startActivity(intent, true /* dismissShade */, controller,
+ true /* showOverLockscreenWhenLocked */);
} else {
- mActivityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */);
+ mActivityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */, controller);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index 2a13e6b..e565a44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -45,7 +45,6 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
import com.android.systemui.statusbar.NotificationMediaManager;
import libcore.io.IoUtils;
@@ -53,7 +52,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Objects;
-import java.util.Optional;
import javax.inject.Inject;
@@ -70,7 +68,6 @@
private final WallpaperManager mWallpaperManager;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final Handler mH;
- private final Optional<FaceAuthScreenBrightnessController> mFaceAuthScreenBrightnessController;
private boolean mCached;
private Bitmap mCache;
@@ -86,14 +83,12 @@
KeyguardUpdateMonitor keyguardUpdateMonitor,
DumpManager dumpManager,
NotificationMediaManager mediaManager,
- Optional<FaceAuthScreenBrightnessController> faceAuthScreenBrightnessController,
@Main Handler mainHandler) {
dumpManager.registerDumpable(getClass().getSimpleName(), this);
mWallpaperManager = wallpaperManager;
mCurrentUserId = ActivityManager.getCurrentUser();
mUpdateMonitor = keyguardUpdateMonitor;
mMediaManager = mediaManager;
- mFaceAuthScreenBrightnessController = faceAuthScreenBrightnessController;
mH = mainHandler;
if (iWallpaperManager != null) {
@@ -132,14 +127,6 @@
return LoaderResult.success(null);
}
- Bitmap faceAuthWallpaper = null;
- if (mFaceAuthScreenBrightnessController.isPresent()) {
- faceAuthWallpaper = mFaceAuthScreenBrightnessController.get().getFaceAuthWallpaper();
- if (faceAuthWallpaper != null) {
- return LoaderResult.success(faceAuthWallpaper);
- }
- }
-
// Prefer the selected user (when specified) over the current user for the FLAG_SET_LOCK
// wallpaper.
final int lockWallpaperUserId =
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 917f132..4ce33b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -468,7 +468,6 @@
private boolean mPulsing;
private boolean mUserSetupComplete;
- private int mQsNotificationTopPadding;
private boolean mHideIconsDuringLaunchAnimation = true;
private int mStackScrollerMeasuringPass;
private ArrayList<Consumer<ExpandableNotificationRow>>
@@ -2360,7 +2359,8 @@
private void updateQsExpansion() {
if (mQs == null) return;
float qsExpansionFraction = computeQsExpansionFraction();
- mQs.setQsExpansion(qsExpansionFraction, getExpandedFraction(), getHeaderTranslation());
+ mQs.setQsExpansion(qsExpansionFraction, getExpandedFraction(), getHeaderTranslation(),
+ mNotificationStackScrollLayoutController.getNotificationSquishinessFraction());
mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
@@ -2522,7 +2522,7 @@
// qsTranslation should only be positive during pulse expansion because it's
// already translating in from the top
qsTranslation = Math.max(0, (top - mQs.getHeader().getHeight()) / 2.0f);
- } else {
+ } else if (!mShouldUseSplitNotificationShade) {
qsTranslation = (top - mQs.getHeader().getHeight()) * QS_PARALLAX_AMOUNT;
}
}
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 a7ecd06..8732891 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -41,15 +41,28 @@
private val iconManager: StatusBarIconController.IconManager
private val qsCarrierGroupController: QSCarrierGroupController
private var visible = false
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+ updateListeners()
+ }
var shadeExpanded = false
set(value) {
+ if (field == value) {
+ return
+ }
field = value
updateVisibility()
}
var splitShadeMode = false
set(value) {
+ if (field == value) {
+ return
+ }
field = value
updateVisibility()
}
@@ -78,15 +91,20 @@
}
private fun updateVisibility() {
- val shouldBeVisible = shadeExpanded && splitShadeMode
- if (visible != shouldBeVisible) {
- visible = shouldBeVisible
- statusBar.visibility = if (shouldBeVisible) View.VISIBLE else View.GONE
- updateListeners(shouldBeVisible)
+ val visibility = if (!splitShadeMode) {
+ View.GONE
+ } else if (shadeExpanded) {
+ View.VISIBLE
+ } else {
+ View.INVISIBLE
+ }
+ if (statusBar.visibility != visibility) {
+ statusBar.visibility = visibility
+ visible = visibility == View.VISIBLE
}
}
- private fun updateListeners(visible: Boolean) {
+ private fun updateListeners() {
qsCarrierGroupController.setListening(visible)
if (visible) {
statusBarIconController.addIconGroup(iconManager)
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 0e75a45..bce6ccc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -207,6 +207,7 @@
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
@@ -535,6 +536,7 @@
private final int[] mAbsPos = new int[2];
+ private final NotifShadeEventSource mNotifShadeEventSource;
protected final NotificationEntryManager mEntryManager;
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
@@ -718,6 +720,7 @@
FalsingManager falsingManager,
FalsingCollector falsingCollector,
BroadcastDispatcher broadcastDispatcher,
+ NotifShadeEventSource notifShadeEventSource,
NotificationEntryManager notificationEntryManager,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
@@ -823,6 +826,7 @@
mFalsingCollector = falsingCollector;
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
+ mNotifShadeEventSource = notifShadeEventSource;
mEntryManager = notificationEntryManager;
mGutsManager = notificationGutsManager;
mNotificationLogger = notificationLogger;
@@ -1506,6 +1510,7 @@
mDynamicPrivacyController,
mKeyguardStateController,
mKeyguardIndicationController,
+ mFeatureFlags,
this /* statusBar */,
mShadeController,
mLockscreenShadeTransitionController,
@@ -1513,6 +1518,7 @@
mViewHierarchyManager,
mLockscreenUserManager,
mStatusBarStateController,
+ mNotifShadeEventSource,
mEntryManager,
mMediaManager,
mGutsManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 16d7f98..6ae8331 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -156,18 +156,21 @@
val isRtl = rotatedResources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
val roundedCornerPadding = rotatedResources
.getDimensionPixelSize(R.dimen.rounded_corner_content_padding)
- val minDotWidth = if (isPrivacyDotEnabled)
+ val minDotPadding = if (isPrivacyDotEnabled)
rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_min_padding)
else 0
+ val dotWidth = if (isPrivacyDotEnabled)
+ rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter)
+ else 0
val minLeft: Int
val minRight: Int
if (isRtl) {
- minLeft = max(minDotWidth, roundedCornerPadding)
+ minLeft = max(minDotPadding, roundedCornerPadding)
minRight = roundedCornerPadding
} else {
minLeft = roundedCornerPadding
- minRight = max(minDotWidth, roundedCornerPadding)
+ minRight = max(minDotPadding, roundedCornerPadding)
}
return calculateInsetsForRotationWithRotatedResources(
@@ -177,7 +180,9 @@
context.resources.configuration.windowConfiguration.maxBounds,
SystemBarUtils.getStatusBarHeight(context),
minLeft,
- minRight)
+ minRight,
+ isRtl,
+ dotWidth)
}
fun getStatusBarPaddingTop(@Rotation rotation: Int? = null): Int {
@@ -246,10 +251,13 @@
*
* @param currentRotation current device rotation
* @param targetRotation rotation for which to calculate the status bar content rect
- * @param displayCutout [DisplayCutout] for the curren display. possibly null
+ * @param displayCutout [DisplayCutout] for the current display. possibly null
* @param windowMetrics [WindowMetrics] for the current window
* @param statusBarHeight height of the status bar for the target rotation
- * @param roundedCornerPadding from rounded_corner_content_padding
+ * @param minLeft the minimum padding to enforce on the left
+ * @param minRight the minimum padding to enforce on the right
+ * @param isRtl current layout direction is Right-To-Left or not
+ * @param dotWidth privacy dot image width (0 if privacy dot is disabled)
*
* @see [RotationUtils#getResourcesForRotation]
*/
@@ -260,7 +268,9 @@
maxBounds: Rect,
statusBarHeight: Int,
minLeft: Int,
- minRight: Int
+ minRight: Int,
+ isRtl: Boolean,
+ dotWidth: Int
): Rect {
/*
TODO: Check if this is ever used for devices with no rounded corners
@@ -279,6 +289,8 @@
maxBounds.height(),
minLeft,
minRight,
+ isRtl,
+ dotWidth,
targetRotation,
currentRotation)
@@ -296,6 +308,8 @@
* @param cHeight display height in our current rotation
* @param minLeft the minimum padding to enforce on the left
* @param minRight the minimum padding to enforce on the right
+ * @param isRtl current layout direction is Right-To-Left or not
+ * @param dotWidth privacy dot image width (0 if privacy dot is disabled)
* @param targetRotation the rotation for which to calculate margins
* @param currentRotation the rotation from which the display cutout was generated
*
@@ -311,6 +325,8 @@
cHeight: Int,
minLeft: Int,
minRight: Int,
+ isRtl: Boolean,
+ dotWidth: Int,
@Rotation targetRotation: Int,
@Rotation currentRotation: Int
): Rect {
@@ -345,13 +361,16 @@
}
if (cutoutRect.touchesLeftEdge(relativeRotation, cWidth, cHeight)) {
-
- val l = max(minLeft, cutoutRect.logicalWidth(relativeRotation))
- leftMargin = max(l, leftMargin)
+ var logicalWidth = cutoutRect.logicalWidth(relativeRotation)
+ if (isRtl) logicalWidth += dotWidth
+ leftMargin = max(logicalWidth, leftMargin)
} else if (cutoutRect.touchesRightEdge(relativeRotation, cWidth, cHeight)) {
- val logicalWidth = cutoutRect.logicalWidth(relativeRotation)
- rightMargin = max(minRight, logicalWidth)
+ var logicalWidth = cutoutRect.logicalWidth(relativeRotation)
+ if (!isRtl) logicalWidth += dotWidth
+ rightMargin = max(rightMargin, logicalWidth)
}
+ // TODO(b/203626889): Fix the scenario when config_mainBuiltInDisplayCutoutRectApproximation
+ // is very close to but not directly touch edges.
}
return Rect(leftMargin, 0, logicalDisplayWidth - rightMargin, sbHeight)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 07489a9..cd95cfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -51,7 +51,6 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dock.DockManager;
-import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -70,7 +69,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Objects;
-import java.util.Optional;
import javax.inject.Inject;
@@ -109,7 +107,6 @@
private final ConfigurationController mConfigurationController;
private final NavigationModeController mNavigationModeController;
private final NotificationShadeWindowController mNotificationShadeWindowController;
- private final Optional<FaceAuthScreenBrightnessController> mFaceAuthScreenBrightnessController;
private final KeyguardBouncer.Factory mKeyguardBouncerFactory;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@@ -242,7 +239,6 @@
DockManager dockManager,
NotificationShadeWindowController notificationShadeWindowController,
KeyguardStateController keyguardStateController,
- Optional<FaceAuthScreenBrightnessController> faceAuthScreenBrightnessController,
NotificationMediaManager notificationMediaManager,
KeyguardBouncer.Factory keyguardBouncerFactory,
WakefulnessLifecycle wakefulnessLifecycle,
@@ -260,7 +256,6 @@
mKeyguardUpdateManager = keyguardUpdateMonitor;
mStatusBarStateController = sysuiStatusBarStateController;
mDockManager = dockManager;
- mFaceAuthScreenBrightnessController = faceAuthScreenBrightnessController;
mKeyguardBouncerFactory = keyguardBouncerFactory;
mWakefulnessLifecycle = wakefulnessLifecycle;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
@@ -285,11 +280,6 @@
mNotificationContainer = notificationContainer;
mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create(
KeyguardMessageArea.findSecurityMessageDisplay(container));
- mFaceAuthScreenBrightnessController.ifPresent((it) -> {
- View overlay = new View(mContext);
- container.addView(overlay);
- it.attach(overlay);
- });
registerListeners();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index c655964..cf9b2c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -17,9 +17,7 @@
import static com.android.systemui.statusbar.phone.StatusBar.CLOSE_PANEL_WHEN_EMPTIED;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG;
-import static com.android.systemui.statusbar.phone.StatusBar.SPEW;
-import android.annotation.Nullable;
import android.app.KeyguardManager;
import android.content.Context;
import android.os.RemoteException;
@@ -36,7 +34,6 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.MessagingGroup;
import com.android.internal.widget.MessagingMessage;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -44,6 +41,7 @@
import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.R;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.CommandQueue;
@@ -59,10 +57,10 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-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.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -88,6 +86,7 @@
private final NotificationViewHierarchyManager mViewHierarchyManager;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final SysuiStatusBarStateController mStatusBarStateController;
+ private final NotifShadeEventSource mNotifShadeEventSource;
private final NotificationEntryManager mEntryManager;
private final NotificationMediaManager mMediaManager;
private final NotificationGutsManager mGutsManager;
@@ -100,6 +99,7 @@
private final DozeScrimController mDozeScrimController;
private final ScrimController mScrimController;
private final KeyguardIndicationController mKeyguardIndicationController;
+ private final FeatureFlags mFeatureFlags;
private final StatusBar mStatusBar;
private final ShadeController mShadeController;
private final LockscreenShadeTransitionController mShadeTransitionController;
@@ -127,6 +127,7 @@
DynamicPrivacyController dynamicPrivacyController,
KeyguardStateController keyguardStateController,
KeyguardIndicationController keyguardIndicationController,
+ FeatureFlags featureFlags,
StatusBar statusBar,
ShadeController shadeController,
LockscreenShadeTransitionController shadeTransitionController,
@@ -134,6 +135,7 @@
NotificationViewHierarchyManager notificationViewHierarchyManager,
NotificationLockscreenUserManager lockscreenUserManager,
SysuiStatusBarStateController sysuiStatusBarStateController,
+ NotifShadeEventSource notifShadeEventSource,
NotificationEntryManager notificationEntryManager,
NotificationMediaManager notificationMediaManager,
NotificationGutsManager notificationGutsManager,
@@ -148,6 +150,7 @@
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
mKeyguardIndicationController = keyguardIndicationController;
+ mFeatureFlags = featureFlags;
// TODO: use KeyguardStateController#isOccluded to remove this dependency
mStatusBar = statusBar;
mShadeController = shadeController;
@@ -156,6 +159,7 @@
mViewHierarchyManager = notificationViewHierarchyManager;
mLockscreenUserManager = lockscreenUserManager;
mStatusBarStateController = sysuiStatusBarStateController;
+ mNotifShadeEventSource = notifShadeEventSource;
mEntryManager = notificationEntryManager;
mMediaManager = notificationMediaManager;
mGutsManager = notificationGutsManager;
@@ -186,30 +190,18 @@
mNotificationPanel.createRemoteInputDelegate());
initController.addPostInitTask(() -> {
- NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
- @Override
- public void onEntryRemoved(
- @Nullable NotificationEntry entry,
- NotificationVisibility visibility,
- boolean removedByUser,
- int reason) {
- StatusBarNotificationPresenter.this.onNotificationRemoved(
- entry.getKey(), entry.getSbn(), reason);
- if (removedByUser) {
- maybeEndAmbientPulse();
- }
- }
- };
-
mKeyguardIndicationController.init();
mViewHierarchyManager.setUpWithPresenter(this,
stackScrollerController.getNotificationListContainer());
- mEntryManager.setUpWithPresenter(this);
- mEntryManager.addNotificationEntryListener(notificationEntryListener);
- mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager);
- mEntryManager.addNotificationLifetimeExtender(mGutsManager);
- mEntryManager.addNotificationLifetimeExtenders(
- remoteInputManager.getLifetimeExtenders());
+ mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
+ mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
+ if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ mEntryManager.setUpWithPresenter(this);
+ mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager);
+ mEntryManager.addNotificationLifetimeExtender(mGutsManager);
+ mEntryManager.addNotificationLifetimeExtenders(
+ remoteInputManager.getLifetimeExtenders());
+ }
notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
mLockscreenUserManager.setUpWithPresenter(this);
mMediaManager.setUpWithPresenter(this);
@@ -226,8 +218,21 @@
configurationController.addCallback(this);
}
+ /** Called when the shade has been emptied to attempt to close the shade */
+ private void maybeClosePanelForShadeEmptied() {
+ if (CLOSE_PANEL_WHEN_EMPTIED
+ && !mNotificationPanel.isTracking()
+ && !mNotificationPanel.isQsExpanded()
+ && mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
+ && !isCollapsing()) {
+ mStatusBarStateController.setState(StatusBarState.KEYGUARD);
+ }
+ }
+
@Override
public void onDensityOrFontScaleChanged() {
+ // TODO(b/145659174): Remove legacy pipeline code
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) return;
MessagingMessage.dropCache();
MessagingGroup.dropCache();
if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
@@ -239,8 +244,10 @@
@Override
public void onUiModeChanged() {
+ // TODO(b/145659174): Remove legacy pipeline code
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) return;
if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
- updateNotificationOnUiModeChanged();
+ updateNotificationsOnUiModeChanged();
} else {
mDispatchUiModeChangeOnUserSwitched = true;
}
@@ -251,7 +258,9 @@
onDensityOrFontScaleChanged();
}
- private void updateNotificationOnUiModeChanged() {
+ private void updateNotificationsOnUiModeChanged() {
+ // TODO(b/145659174): Remove legacy pipeline code
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) return;
List<NotificationEntry> userNotifications =
mEntryManager.getActiveNotificationsForCurrentUser();
for (int i = 0; i < userNotifications.size(); i++) {
@@ -264,6 +273,8 @@
}
private void updateNotificationsOnDensityOrFontScaleChanged() {
+ // TODO(b/145659174): Remove legacy pipeline code
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) return;
List<NotificationEntry> userNotifications =
mEntryManager.getActiveNotificationsForCurrentUser();
for (int i = 0; i < userNotifications.size(); i++) {
@@ -276,6 +287,7 @@
}
}
+
@Override
public boolean isCollapsing() {
return mNotificationPanel.isCollapsing()
@@ -308,21 +320,6 @@
mNotificationPanel.updateNotificationViews(reason);
}
- private void onNotificationRemoved(String key, StatusBarNotification old, int reason) {
- if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
-
- if (old != null && CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
- && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()
- && mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
- && !isCollapsing()) {
- mStatusBarStateController.setState(StatusBarState.KEYGUARD);
- }
- }
-
- public boolean hasActiveNotifications() {
- return mEntryManager.hasActiveNotifications();
- }
-
@Override
public void onUserSwitched(int newUserId) {
// Begin old BaseStatusBar.userSwitched
@@ -335,7 +332,7 @@
mReinflateNotificationsOnUserSwitched = false;
}
if (mDispatchUiModeChangeOnUserSwitched) {
- updateNotificationOnUiModeChanged();
+ updateNotificationsOnUiModeChanged();
mDispatchUiModeChangeOnUserSwitched = false;
}
updateNotificationViews("user switched");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index c452a48..2681d5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -68,6 +68,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
@@ -157,6 +158,7 @@
FalsingManager falsingManager,
FalsingCollector falsingCollector,
BroadcastDispatcher broadcastDispatcher,
+ NotifShadeEventSource notifShadeEventSource,
NotificationEntryManager notificationEntryManager,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
@@ -260,6 +262,7 @@
falsingManager,
falsingCollector,
broadcastDispatcher,
+ notifShadeEventSource,
notificationEntryManager,
notificationGutsManager,
notificationLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
index 89297fd..d5d3efd 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
@@ -26,6 +26,7 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
+import android.view.WindowManager;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
@@ -47,6 +48,8 @@
@Override
public void onCreate(Bundle icicle) {
+ getWindow().addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
super.onCreate(icicle);
Intent intent = getIntent();
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java
index 4850a02..2871263 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java
@@ -30,6 +30,7 @@
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.util.Log;
+import android.view.WindowManager;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
@@ -51,6 +52,8 @@
@Override
public void onCreate(Bundle icicle) {
+ getWindow().addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
super.onCreate(icicle);
if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
index c420a6d..02a7637 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
@@ -33,6 +33,7 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Log;
+import android.view.WindowManager;
import android.widget.CheckBox;
import com.android.internal.app.ResolverActivity;
@@ -60,6 +61,8 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
+ getWindow().addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
Intent intent = getIntent();
Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
if (!(targetParcelable instanceof Intent)) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index fd9783a..0a33930 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -463,13 +463,13 @@
@ShellMainThread ShellExecutor mainExecutor,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool,
+ TransactionPool transactionPool, IconProvider iconProvider,
Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
- displayInsetsController, transitions,
- transactionPool, stageTaskUnfoldControllerProvider));
+ displayInsetsController, transitions, transactionPool, iconProvider,
+ stageTaskUnfoldControllerProvider));
} else {
return Optional.empty();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
index 5fee7fb..ca7d506 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
@@ -18,6 +18,7 @@
import android.graphics.Rect
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN
import android.hardware.biometrics.SensorProperties
import android.hardware.display.DisplayManager
@@ -182,7 +183,16 @@
@Test
fun testIgnoredForKeyguard() {
- overlayController.show(SENSOR_ID, REASON_AUTH_KEYGUARD)
+ testIgnoredFor(REASON_AUTH_KEYGUARD)
+ }
+
+ @Test
+ fun testIgnoredForSettings() {
+ testIgnoredFor(REASON_AUTH_SETTINGS)
+ }
+
+ private fun testIgnoredFor(reason: Int) {
+ overlayController.show(SENSOR_ID, reason)
executor.runAllReady()
verify(windowManager, never()).addView(any(), any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
index 172dcda..b3c098c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
@@ -18,23 +18,35 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
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;
+
@SmallTest
public class FeatureFlagManagerTest extends SysuiTestCase {
FeatureFlagManager mFeatureFlagManager;
+ @Mock private DumpManager mDumpManager;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mFeatureFlagManager = new FeatureFlagManager();
+ mFeatureFlagManager = new FeatureFlagManager(mDumpManager);
}
@Test
@@ -43,4 +55,31 @@
// Again, nothing changes.
assertThat(mFeatureFlagManager.isEnabled(1, false)).isFalse();
}
+
+ @Test
+ public void testDump() {
+ // Even if a flag is set before
+ mFeatureFlagManager.setEnabled(1, true);
+
+ // WHEN the flags have been accessed
+ assertFalse(mFeatureFlagManager.isEnabled(1, false));
+ assertTrue(mFeatureFlagManager.isEnabled(2, true));
+
+ // Even if a flag is set after
+ mFeatureFlagManager.setEnabled(2, false);
+
+ // 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_2: true\n");
+ }
+
+ private String dumpToString() {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ mFeatureFlagManager.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/keyguard/FaceAuthScreenBrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt
deleted file mode 100644
index cb05a6b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard
-
-import android.animation.ValueAnimator
-import android.content.res.Resources
-import android.hardware.biometrics.BiometricSourceType
-import android.os.Handler
-import android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT
-import android.testing.AndroidTestingRunner
-import android.util.TypedValue
-import android.view.View
-import androidx.test.filters.SmallTest
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Dumpable
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.NotificationShadeWindowController
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.settings.GlobalSettings
-import com.android.systemui.util.settings.SystemSettings
-import org.junit.Before
-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.`when`
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.anyString
-import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-const val INITIAL_BRIGHTNESS = 0.5f
-
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-class FaceAuthScreenBrightnessControllerTest : SysuiTestCase() {
-
- @Mock
- lateinit var whiteOverlay: View
- @Mock
- lateinit var dumpManager: DumpManager
- @Mock
- lateinit var resources: Resources
- @Mock
- lateinit var mainHandler: Handler
- @Mock
- lateinit var globalSettings: GlobalSettings
- @Mock
- lateinit var systemSettings: SystemSettings
- @Mock
- lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock
- lateinit var notificationShadeWindowController: NotificationShadeWindowController
- @Mock
- lateinit var animator: ValueAnimator
- @Captor
- lateinit var keyguardUpdateCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
- lateinit var faceAuthScreenBrightnessController: FaceAuthScreenBrightnessController
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- faceAuthScreenBrightnessController = object : FaceAuthScreenBrightnessController(
- notificationShadeWindowController, keyguardUpdateMonitor, resources, globalSettings,
- systemSettings, mainHandler, dumpManager, true) {
- override fun createAnimator(start: Float, end: Float) = animator
- }
- `when`(systemSettings.getFloat(eq(SCREEN_BRIGHTNESS_FLOAT))).thenReturn(INITIAL_BRIGHTNESS)
- `when`(systemSettings.getFloat(eq(SCREEN_BRIGHTNESS_FLOAT), eq(1f)))
- .thenReturn(INITIAL_BRIGHTNESS)
- faceAuthScreenBrightnessController.attach(whiteOverlay)
- verify(keyguardUpdateMonitor).registerCallback(capture(keyguardUpdateCallback))
- }
-
- @Test
- fun init_registersDumpManager() {
- verify(dumpManager).registerDumpable(anyString(), any(Dumpable::class.java))
- }
-
- @Test
- fun init_registersKeyguardCallback() {
- verify(keyguardUpdateMonitor)
- .registerCallback(any(KeyguardUpdateMonitorCallback::class.java))
- }
-
- @Test
- fun onBiometricRunningChanged_animatesBrightness() {
- clearInvocations(whiteOverlay)
- keyguardUpdateCallback.value
- .onBiometricRunningStateChanged(true, BiometricSourceType.FACE)
- verify(whiteOverlay).visibility = eq(View.VISIBLE)
- verify(animator).start()
- }
-
- @Test
- fun faceAuthWallpaper_whenFaceIsDisabledForUser() {
- faceAuthScreenBrightnessController.useFaceAuthWallpaper = true
- faceAuthScreenBrightnessController.faceAuthWallpaper
- verify(resources, never()).openRawResource(anyInt(), any(TypedValue::class.java))
- }
-
- @Test
- fun faceAuthWallpaper_whenFaceFlagIsDisabled() {
- faceAuthScreenBrightnessController.useFaceAuthWallpaper = true
- faceAuthScreenBrightnessController.faceAuthWallpaper
- verify(resources, never()).openRawResource(anyInt(), any(TypedValue::class.java))
- }
-
- @Test
- fun faceAuthWallpaper_whenFaceIsEnabledForUser() {
- faceAuthScreenBrightnessController.useFaceAuthWallpaper = true
- `when`(keyguardUpdateMonitor.isFaceAuthEnabledForUser(anyInt())).thenReturn(true)
- faceAuthScreenBrightnessController.faceAuthWallpaper
- verify(resources).openRawResource(anyInt(), any(TypedValue::class.java))
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 750600ad..52173c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -212,12 +212,14 @@
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(
- mContext.getString(R.string.media_output_dialog_disconnected, TEST_DEVICE_NAME_2));
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTwoLineTitleText.getText().toString()).isEqualTo(
+ TEST_DEVICE_NAME_2);
+ assertThat(mViewHolder.mSubTitleText.getText().toString()).isEqualTo(
+ mContext.getString(R.string.media_output_dialog_disconnected));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
new file mode 100644
index 0000000..3059aa1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
@@ -0,0 +1,62 @@
+package com.android.systemui.qs
+
+import android.testing.AndroidTestingRunner
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tileimpl.QSTileViewImpl
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class QSSquishinessControllerTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsTileHost: QSTileHost
+ @Mock private lateinit var qqsFooterActionsView: FooterActionsView
+ @Mock private lateinit var qqsFooterActionsViewLP: ViewGroup.MarginLayoutParams
+ @Mock private lateinit var qsAnimator: QSAnimator
+ @Mock private lateinit var quickQsPanelController: QuickQSPanelController
+ @Mock private lateinit var qstileView: QSTileViewImpl
+ @Mock private lateinit var qstile: QSTile
+ @Mock private lateinit var tileLayout: TileLayout
+
+ @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
+
+ private lateinit var qsSquishinessController: QSSquishinessController
+
+ @Before
+ fun setup() {
+ qsSquishinessController = QSSquishinessController(qsTileHost, qqsFooterActionsView,
+ qsAnimator, quickQsPanelController)
+ `when`(qsTileHost.tiles).thenReturn(mutableListOf(qstile))
+ `when`(quickQsPanelController.getTileView(any())).thenReturn(qstileView)
+ `when`(quickQsPanelController.tileLayout).thenReturn(tileLayout)
+ `when`(qqsFooterActionsView.layoutParams).thenReturn(qqsFooterActionsViewLP)
+ }
+
+ @Test
+ fun setSquishiness_requestsAnimatorUpdate() {
+ qsSquishinessController.squishiness = 0.5f
+ verify(qsAnimator, never()).requestAnimatorUpdate()
+
+ qsSquishinessController.squishiness = 0f
+ verify(qsAnimator).requestAnimatorUpdate()
+ }
+
+ @Test
+ fun setSquishiness_updatesTiles() {
+ qsSquishinessController.squishiness = 0.5f
+ verify(qstileView).squishinessFraction = 0.5f
+ verify(tileLayout).setSquishinessFraction(0.5f)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 94af10a..98c7274 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -52,13 +52,11 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.doNothing
-import org.mockito.Mockito.never
import org.mockito.Mockito.nullable
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
@@ -272,28 +270,7 @@
}
@Test
- fun handleClick_availableAndLocked_activityStarted() {
- verify(controlsListingController).observe(
- any(LifecycleOwner::class.java),
- capture(listingCallbackCaptor)
- )
- `when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
- `when`(keyguardStateController.isUnlocked).thenReturn(false)
-
- listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
- testableLooper.processAllMessages()
-
- tile.click(null /* view */)
- testableLooper.processAllMessages()
-
- // The activity should be started right away and not require a keyguard dismiss.
- verifyZeroInteractions(activityStarter)
- verify(spiedContext).startActivity(intentCaptor.capture())
- assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME)
- }
-
- @Test
- fun handleClick_availableAndUnlocked_activityStarted() {
+ fun handleClick_available_shownOverLockscreenWhenLocked() {
verify(controlsListingController).observe(
any(LifecycleOwner::class.java),
capture(listingCallbackCaptor)
@@ -307,16 +284,16 @@
tile.click(null /* view */)
testableLooper.processAllMessages()
- verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
verify(activityStarter).startActivity(
intentCaptor.capture(),
eq(true) /* dismissShade */,
- nullable(ActivityLaunchAnimator.Controller::class.java))
+ nullable(ActivityLaunchAnimator.Controller::class.java),
+ eq(true) /* showOverLockscreenWhenLocked */)
assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME)
}
@Test
- fun handleClick_availableAfterUnlockAndIsLocked_keyguardDismissRequired() {
+ fun handleClick_availableAfterUnlock_notShownOverLockscreenWhenLocked() {
verify(controlsListingController).observe(
any(LifecycleOwner::class.java),
capture(listingCallbackCaptor)
@@ -331,38 +308,11 @@
tile.click(null /* view */)
testableLooper.processAllMessages()
- verify(activityStarter, never()).startActivity(
- any(),
- anyBoolean() /* dismissShade */,
- nullable(ActivityLaunchAnimator.Controller::class.java))
- verify(activityStarter).postStartActivityDismissingKeyguard(
- intentCaptor.capture(),
- anyInt(),
- nullable(ActivityLaunchAnimator.Controller::class.java))
- assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME)
- }
-
- @Test
- fun handleClick_availableAfterUnlockAndIsUnlocked_activityStarted() {
- verify(controlsListingController).observe(
- any(LifecycleOwner::class.java),
- capture(listingCallbackCaptor)
- )
- `when`(controlsComponent.getVisibility())
- .thenReturn(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK)
- `when`(keyguardStateController.isUnlocked).thenReturn(true)
-
- listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
- testableLooper.processAllMessages()
-
- tile.click(null /* view */)
- testableLooper.processAllMessages()
-
- verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
verify(activityStarter).startActivity(
intentCaptor.capture(),
- eq(true) /* dismissShade */,
- nullable(ActivityLaunchAnimator.Controller::class.java))
+ anyBoolean() /* dismissShade */,
+ nullable(ActivityLaunchAnimator.Controller::class.java),
+ eq(false) /* showOverLockscreenWhenLocked */)
assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME)
}
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 fe32839..5cea763 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
@@ -51,6 +51,7 @@
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.toast.SystemUIToast;
import com.android.systemui.toast.ToastFactory;
import com.android.systemui.util.CarrierConfigTracker;
@@ -135,6 +136,8 @@
private Animator mAnimator;
@Mock
private CarrierConfigTracker mCarrierConfigTracker;
+ @Mock
+ private LocationController mLocationController;
private TestableResources mTestableResources;
private MockInternetDialogController mInternetDialogController;
@@ -170,7 +173,8 @@
mSubscriptionManager, mTelephonyManager, mWifiManager,
mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher,
mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController,
- mWindowManager, mToastFactory, mWorkerHandler, mCarrierConfigTracker);
+ mWindowManager, mToastFactory, mWorkerHandler, mCarrierConfigTracker,
+ mLocationController);
mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
mInternetDialogController.mOnSubscriptionsChangedListener);
mInternetDialogController.onStart(mInternetDialogCallback, true);
@@ -602,6 +606,30 @@
verify(mMergedCarrierEntry).setEnabled(false);
}
+ @Test
+ public void isWifiScanEnabled_locationOff_returnFalse() {
+ when(mLocationController.isLocationEnabled()).thenReturn(false);
+ when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false);
+
+ assertThat(mInternetDialogController.isWifiScanEnabled()).isFalse();
+
+ when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true);
+
+ assertThat(mInternetDialogController.isWifiScanEnabled()).isFalse();
+ }
+
+ @Test
+ public void isWifiScanEnabled_locationOn_returnIsScanAlwaysAvailable() {
+ when(mLocationController.isLocationEnabled()).thenReturn(true);
+ when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false);
+
+ assertThat(mInternetDialogController.isWifiScanEnabled()).isFalse();
+
+ when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true);
+
+ assertThat(mInternetDialogController.isWifiScanEnabled()).isTrue();
+ }
+
private String getResourcesString(String name) {
return mContext.getResources().getString(getResourcesId(name));
}
@@ -625,12 +653,13 @@
KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings,
KeyguardStateController keyguardStateController, WindowManager windowManager,
ToastFactory toastFactory, Handler workerHandler,
- CarrierConfigTracker carrierConfigTracker) {
+ CarrierConfigTracker carrierConfigTracker,
+ LocationController locationController) {
super(context, uiEventLogger, starter, accessPointController, subscriptionManager,
telephonyManager, wifiManager, connectivityManager, handler, mainExecutor,
broadcastDispatcher, keyguardUpdateMonitor, globalSettings,
keyguardStateController, windowManager, toastFactory, workerHandler,
- carrierConfigTracker);
+ carrierConfigTracker, locationController);
mGlobalSettings = globalSettings;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index 6688960..5e1fea5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -282,7 +282,7 @@
@Test
public void updateDialog_wifiOffAndWifiScanOff_hideWifiScanNotify() {
when(mWifiManager.isWifiEnabled()).thenReturn(false);
- when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false);
+ when(mInternetDialogController.isWifiScanEnabled()).thenReturn(false);
mInternetDialog.updateDialog(false);
@@ -292,7 +292,7 @@
@Test
public void updateDialog_wifiOffAndWifiScanOnAndDeviceLocked_hideWifiScanNotify() {
when(mWifiManager.isWifiEnabled()).thenReturn(false);
- when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true);
+ when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true);
when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
mInternetDialog.updateDialog(false);
@@ -303,7 +303,7 @@
@Test
public void updateDialog_wifiOffAndWifiScanOnAndDeviceUnlocked_showWifiScanNotify() {
when(mWifiManager.isWifiEnabled()).thenReturn(false);
- when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true);
+ when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true);
when(mInternetDialogController.isDeviceLocked()).thenReturn(false);
mInternetDialog.updateDialog(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
new file mode 100644
index 0000000..5915cd7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.service.notification.NotificationListenerService.REASON_APP_CANCEL
+import android.service.notification.NotificationListenerService.REASON_CANCEL
+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.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.OnBeforeRenderListListener
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.util.mockito.argumentCaptor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class ShadeEventCoordinatorTest : SysuiTestCase() {
+ private lateinit var coordinator: ShadeEventCoordinator
+ private lateinit var notifCollectionListener: NotifCollectionListener
+ private lateinit var onBeforeRenderListListener: OnBeforeRenderListListener
+
+ private lateinit var entry1: NotificationEntry
+ private lateinit var entry2: NotificationEntry
+
+ @Mock private lateinit var pipeline: NotifPipeline
+ @Mock private lateinit var logger: ShadeEventCoordinatorLogger
+ @Mock private lateinit var notifRemovedByUserCallback: Runnable
+ @Mock private lateinit var shadeEmptiedCallback: Runnable
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ coordinator = ShadeEventCoordinator(logger)
+ coordinator.attach(pipeline)
+ notifCollectionListener = argumentCaptor<NotifCollectionListener>().let {
+ verify(pipeline).addCollectionListener(it.capture())
+ it.value!!
+ }
+ onBeforeRenderListListener = argumentCaptor<OnBeforeRenderListListener>().let {
+ verify(pipeline).addOnBeforeRenderListListener(it.capture())
+ it.value!!
+ }
+ coordinator.setNotifRemovedByUserCallback(notifRemovedByUserCallback)
+ coordinator.setShadeEmptiedCallback(shadeEmptiedCallback)
+ entry1 = NotificationEntryBuilder().setId(1).build()
+ entry2 = NotificationEntryBuilder().setId(2).build()
+ }
+
+ @Test
+ fun testUserCancelLastNotification() {
+ notifCollectionListener.onEntryRemoved(entry1, REASON_CANCEL)
+ verify(shadeEmptiedCallback, never()).run()
+ verify(notifRemovedByUserCallback, never()).run()
+ onBeforeRenderListListener.onBeforeRenderList(listOf())
+ verify(shadeEmptiedCallback).run()
+ verify(notifRemovedByUserCallback).run()
+ }
+
+ @Test
+ fun testAppCancelLastNotification() {
+ notifCollectionListener.onEntryRemoved(entry1, REASON_APP_CANCEL)
+ onBeforeRenderListListener.onBeforeRenderList(listOf())
+ verify(shadeEmptiedCallback).run()
+ verify(notifRemovedByUserCallback, never()).run()
+ }
+
+ @Test
+ fun testUserCancelOneOfTwoNotifications() {
+ notifCollectionListener.onEntryRemoved(entry1, REASON_CANCEL)
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry2))
+ verify(shadeEmptiedCallback, never()).run()
+ verify(notifRemovedByUserCallback).run()
+ }
+
+ @Test
+ fun testAppCancelOneOfTwoNotifications() {
+ notifCollectionListener.onEntryRemoved(entry1, REASON_APP_CANCEL)
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry2))
+ verify(shadeEmptiedCallback, never()).run()
+ verify(notifRemovedByUserCallback, never()).run()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
new file mode 100644
index 0000000..a9e8164
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
@@ -0,0 +1,87 @@
+package com.android.systemui.statusbar.phone
+
+import android.content.Context
+import android.content.res.Resources
+import android.test.suitebuilder.annotation.SmallTest
+import android.view.View
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ShadeInterpolation
+import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.qs.carrier.QSCarrierGroupController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+class SplitShadeHeaderControllerTest : SysuiTestCase() {
+
+ @Mock private lateinit var view: View
+ @Mock private lateinit var statusIcons: StatusIconContainer
+ @Mock private lateinit var statusBarIconController: StatusBarIconController
+ @Mock private lateinit var qsCarrierGroupController: QSCarrierGroupController
+ @Mock private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
+ @Mock private lateinit var featureFlags: FeatureFlags
+ @Mock private lateinit var batteryMeterView: BatteryMeterView
+ @Mock private lateinit var batteryMeterViewController: BatteryMeterViewController
+ @Mock private lateinit var resources: Resources
+ @Mock private lateinit var context: Context
+ @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
+ var viewVisibility = View.GONE
+
+ private lateinit var splitShadeHeaderController: SplitShadeHeaderController
+
+ @Before
+ fun setup() {
+ whenever<BatteryMeterView>(view.findViewById(R.id.batteryRemainingIcon))
+ .thenReturn(batteryMeterView)
+ whenever<StatusIconContainer>(view.findViewById(R.id.statusIcons)).thenReturn(statusIcons)
+ whenever(statusIcons.context).thenReturn(context)
+ whenever(context.resources).thenReturn(resources)
+ whenever(qsCarrierGroupControllerBuilder.setQSCarrierGroup(any()))
+ .thenReturn(qsCarrierGroupControllerBuilder)
+ whenever(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
+ whenever(view.setVisibility(anyInt())).then {
+ viewVisibility = it.arguments[0] as Int
+ null
+ }
+ whenever(view.visibility).thenAnswer { _ -> viewVisibility }
+ splitShadeHeaderController = SplitShadeHeaderController(view, statusBarIconController,
+ qsCarrierGroupControllerBuilder, featureFlags, batteryMeterViewController)
+ }
+
+ @Test
+ fun setVisible_onlyInSplitShade() {
+ splitShadeHeaderController.splitShadeMode = true
+ splitShadeHeaderController.shadeExpanded = true
+ assertThat(viewVisibility).isEqualTo(View.VISIBLE)
+
+ splitShadeHeaderController.splitShadeMode = false
+ assertThat(viewVisibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun updateListeners_registersWhenVisible() {
+ splitShadeHeaderController.splitShadeMode = true
+ splitShadeHeaderController.shadeExpanded = true
+ verify(qsCarrierGroupController).setListening(true)
+ verify(statusBarIconController).addIconGroup(any())
+ }
+
+ @Test
+ fun shadeExpandedFraction_updatesAlpha() {
+ splitShadeHeaderController.splitShadeMode = true
+ splitShadeHeaderController.shadeExpanded = true
+ splitShadeHeaderController.shadeExpandedFraction = 0.5f
+ verify(view).setAlpha(ShadeInterpolation.getContentAlpha(0.5f))
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index 1503af8..e5158e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -86,7 +86,9 @@
screenBounds,
sbHeightPortrait,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
/* 1080 - 20 (rounded corner) - 30 (chip),
@@ -115,7 +117,9 @@
screenBounds,
sbHeightLandscape,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
/* 2160 - 20 (rounded corner) - 30 (chip),
@@ -146,6 +150,8 @@
val sbHeightPortrait = 100
val sbHeightLandscape = 60
val currentRotation = ROTATION_NONE
+ val isRtl = false
+ val dotWidth = 10
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
@@ -164,7 +170,9 @@
screenBounds,
sbHeightPortrait,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -181,7 +189,9 @@
screenBounds,
sbHeightLandscape,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -200,7 +210,9 @@
screenBounds,
sbHeightPortrait,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -208,7 +220,7 @@
targetRotation = ROTATION_SEASCAPE
expectedBounds = Rect(minLeftPadding,
0,
- screenBounds.height() - dcBounds.height(),
+ screenBounds.height() - dcBounds.height() - dotWidth,
sbHeightLandscape)
bounds = calculateInsetsForRotationWithRotatedResources(
@@ -218,7 +230,9 @@
screenBounds,
sbHeightLandscape,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -237,6 +251,8 @@
val sbHeightPortrait = 100
val sbHeightLandscape = 60
val currentRotation = ROTATION_NONE
+ val isRtl = false
+ val dotWidth = 10
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
@@ -255,7 +271,9 @@
screenBounds,
sbHeightPortrait,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -272,7 +290,9 @@
screenBounds,
sbHeightLandscape,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -289,14 +309,16 @@
screenBounds,
sbHeightPortrait,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_SEASCAPE
expectedBounds = Rect(minLeftPadding,
0,
- screenBounds.height() - dcBounds.height(),
+ screenBounds.height() - dcBounds.height() - dotWidth,
sbHeightLandscape)
bounds = calculateInsetsForRotationWithRotatedResources(
@@ -306,7 +328,9 @@
screenBounds,
sbHeightLandscape,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -320,6 +344,8 @@
val minRightPadding = 20
val sbHeightPortrait = 100
val sbHeightLandscape = 60
+ val isRtl = false
+ val dotWidth = 10
// THEN content insets should only use rounded corner padding
var targetRotation = ROTATION_NONE
@@ -335,7 +361,9 @@
screenBounds,
sbHeightPortrait,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
@@ -351,7 +379,9 @@
screenBounds,
sbHeightLandscape,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_UPSIDE_DOWN
@@ -367,7 +397,9 @@
screenBounds,
sbHeightPortrait,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
@@ -383,7 +415,9 @@
screenBounds,
sbHeightLandscape,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -397,6 +431,8 @@
val sbHeightPortrait = 100
val sbHeightLandscape = 60
val currentRotation = ROTATION_NONE
+ val isRtl = false
+ val dotWidth = 10
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
@@ -414,7 +450,9 @@
screenBounds,
sbHeightPortrait,
minLeftPadding,
- minRightPadding)
+ minRightPadding,
+ isRtl,
+ dotWidth)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 3fcd071..35d15af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -44,7 +44,6 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -60,8 +59,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Optional;
-
import dagger.Lazy;
@SmallTest
@@ -92,8 +89,6 @@
@Mock
private KeyguardBypassController mBypassController;
@Mock
- private FaceAuthScreenBrightnessController mFaceAuthScreenBrightnessController;
- @Mock
private KeyguardBouncer.Factory mKeyguardBouncerFactory;
@Mock
private KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
@@ -133,7 +128,6 @@
mock(DockManager.class),
mock(NotificationShadeWindowController.class),
mKeyguardStateController,
- Optional.of(mFaceAuthScreenBrightnessController),
mock(NotificationMediaManager.class),
mKeyguardBouncerFactory,
mWakefulnessLifecycle,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index c80c072..4e6b0a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -37,6 +37,7 @@
import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -51,6 +52,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -109,12 +111,15 @@
mock(DozeScrimController.class), mock(ScrimController.class),
mock(NotificationShadeWindowController.class), mock(DynamicPrivacyController.class),
mock(KeyguardStateController.class),
- mock(KeyguardIndicationController.class), mStatusBar,
+ mock(KeyguardIndicationController.class),
+ mock(FeatureFlags.class),
+ mStatusBar,
mock(ShadeControllerImpl.class), mock(LockscreenShadeTransitionController.class),
mCommandQueue,
mock(NotificationViewHierarchyManager.class),
mock(NotificationLockscreenUserManager.class),
mock(SysuiStatusBarStateController.class),
+ mock(NotifShadeEventSource.class),
mock(NotificationEntryManager.class),
mock(NotificationMediaManager.class),
mock(NotificationGutsManager.class),
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 943d3c7..ca8b6c8 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
@@ -121,6 +121,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
@@ -210,6 +211,7 @@
@Mock private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private AssistManager mAssistManager;
+ @Mock private NotifShadeEventSource mNotifShadeEventSource;
@Mock private NotificationEntryManager mNotificationEntryManager;
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private NotificationMediaManager mNotificationMediaManager;
@@ -377,6 +379,7 @@
new FalsingManagerFake(),
new FalsingCollectorFake(),
mBroadcastDispatcher,
+ mNotifShadeEventSource,
mNotificationEntryManager,
mNotificationGutsManager,
notificationLogger,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8205d35..e6f91a25f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -128,6 +128,7 @@
import com.android.server.SystemService;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
+import com.android.server.accessibility.magnification.MagnificationScaleProvider;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -338,7 +339,8 @@
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this, mTraceManager);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
- mMagnificationController = new MagnificationController(this, mLock, mContext);
+ mMagnificationController = new MagnificationController(this, mLock, mContext,
+ new MagnificationScaleProvider(mContext));
mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
init();
}
@@ -1364,6 +1366,7 @@
}
private void switchUser(int userId) {
+ mMagnificationController.updateUserIdIfNeeded(userId);
synchronized (mLock) {
if (mCurrentUserId == userId && mInitialized) {
return;
@@ -1386,8 +1389,6 @@
// The user changed.
mCurrentUserId = userId;
-
- mMagnificationController.updateUserIdIfNeeded(mCurrentUserId);
AccessibilityUserState userState = getCurrentUserStateLocked();
readConfigurationForUserStateLocked(userState);
@@ -1444,6 +1445,7 @@
synchronized (mLock) {
mUserStates.remove(userId);
}
+ getMagnificationController().onUserRemoved(userId);
}
// Called only during settings restore; currently supports only the owner user
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 8f30aa9a..c62473d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -28,10 +28,8 @@
import android.content.IntentFilter;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
-import android.provider.Settings;
import android.text.TextUtils;
import android.util.MathUtils;
import android.util.Slog;
@@ -59,7 +57,8 @@
* holding the current state of magnification and animation, and it handles
* communication between the accessibility manager and window manager.
*
- * Magnification is limited to the range [MIN_SCALE, MAX_SCALE], and can only occur inside the
+ * Magnification is limited to the range controlled by
+ * {@link MagnificationScaleProvider#constrainScale(float)}, and can only occur inside the
* magnification region. If a value is out of bounds, it will be adjusted to guarantee these
* constraints.
*/
@@ -69,13 +68,9 @@
private static final MagnificationAnimationCallback STUB_ANIMATION_CALLBACK = success -> {
};
- public static final float MIN_SCALE = 1.0f;
- public static final float MAX_SCALE = 8.0f;
private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false;
- private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f;
-
private final Object mLock;
private final ControllerContext mControllerCtx;
@@ -84,7 +79,7 @@
private final MagnificationInfoChangedCallback mMagnificationInfoChangedCallback;
- private int mUserId;
+ private final MagnificationScaleProvider mScaleProvider;
private final long mMainThreadId;
@@ -489,7 +484,7 @@
return false;
}
// Constrain scale immediately for use in the pivot calculations.
- scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+ scale = MagnificationScaleProvider.constrainScale(scale);
final Rect viewport = mTempRect;
mMagnificationRegion.getBounds(viewport);
@@ -557,7 +552,7 @@
// Compute changes.
boolean changed = false;
- final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+ final float normScale = MagnificationScaleProvider.constrainScale(scale);
if (Float.compare(mCurrentMagnificationSpec.scale, normScale) != 0) {
mCurrentMagnificationSpec.scale = normScale;
changed = true;
@@ -658,12 +653,13 @@
*/
public FullScreenMagnificationController(@NonNull Context context,
@NonNull AccessibilityManagerService ams, @NonNull Object lock,
- @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback) {
+ @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback,
+ @NonNull MagnificationScaleProvider scaleProvider) {
this(new ControllerContext(context, ams,
LocalServices.getService(WindowManagerInternal.class),
new Handler(context.getMainLooper()),
context.getResources().getInteger(R.integer.config_longAnimTime)), lock,
- magnificationInfoChangedCallback);
+ magnificationInfoChangedCallback, scaleProvider);
}
/**
@@ -672,12 +668,14 @@
@VisibleForTesting
public FullScreenMagnificationController(@NonNull ControllerContext ctx,
@NonNull Object lock,
- @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback) {
+ @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback,
+ @NonNull MagnificationScaleProvider scaleProvider) {
mControllerCtx = ctx;
mLock = lock;
mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId();
mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this);
mMagnificationInfoChangedCallback = magnificationInfoChangedCallback;
+ mScaleProvider = scaleProvider;
}
/**
@@ -1096,18 +1094,9 @@
/**
* Persists the default display magnification scale to the current user's settings.
*/
- public void persistScale() {
- // TODO: b/123047354, Need support multi-display?
+ public void persistScale(int displayId) {
final float scale = getScale(Display.DEFAULT_DISPLAY);
- final int userId = mUserId;
-
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- mControllerCtx.putMagnificationScale(scale, userId);
- return null;
- }
- }.execute();
+ mScaleProvider.putScale(scale, displayId);
}
/**
@@ -1117,21 +1106,8 @@
* @return the previously persisted magnification scale, or the default
* scale if none is available
*/
- public float getPersistedScale() {
- return mControllerCtx.getMagnificationScale(mUserId);
- }
-
- /**
- * Sets the currently active user ID.
- *
- * @param userId the currently active user ID
- */
- public void setUserId(int userId) {
- if (mUserId == userId) {
- return;
- }
- mUserId = userId;
- resetAllIfNeeded(false);
+ public float getPersistedScale(int displayId) {
+ return mScaleProvider.getScale(displayId);
}
/**
@@ -1225,7 +1201,11 @@
mControllerCtx.getHandler().sendMessage(m);
}
- private void resetAllIfNeeded(boolean animate) {
+ /**
+ * Resets magnification on all displays.
+ * @param animate reset the magnification with animation
+ */
+ void resetAllIfNeeded(boolean animate) {
synchronized (mLock) {
for (int i = 0; i < mDisplays.size(); i++) {
resetIfNeeded(mDisplays.keyAt(i), animate);
@@ -1288,8 +1268,8 @@
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("MagnificationController[");
- builder.append("mUserId=").append(mUserId);
builder.append(", mDisplays=").append(mDisplays);
+ builder.append(", mScaleProvider=").append(mScaleProvider);
builder.append("]");
return builder.toString();
}
@@ -1570,23 +1550,6 @@
}
/**
- * Write Settings of magnification scale.
- */
- public void putMagnificationScale(float value, int userId) {
- Settings.Secure.putFloatForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, value, userId);
- }
-
- /**
- * Get Settings of magnification scale.
- */
- public float getMagnificationScale(int userId) {
- return Settings.Secure.getFloatForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
- DEFAULT_MAGNIFICATION_SCALE, userId);
- }
-
- /**
* @return Configuration of animation duration.
*/
public long getAnimationDuration() {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 8f4a5cb..935df99 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -119,11 +119,11 @@
private static final boolean DEBUG_DETECTING = false | DEBUG_ALL;
private static final boolean DEBUG_PANNING_SCALING = false | DEBUG_ALL;
- // The MIN_SCALE is different from MagnificationController.MIN_SCALE due
+ // The MIN_SCALE is different from MagnificationScaleProvider.MIN_SCALE due
// to AccessibilityService.MagnificationController#setScale() has
// different scale range
private static final float MIN_SCALE = 2.0f;
- private static final float MAX_SCALE = FullScreenMagnificationController.MAX_SCALE;
+ private static final float MAX_SCALE = MagnificationScaleProvider.MAX_SCALE;
@VisibleForTesting final FullScreenMagnificationController mFullScreenMagnificationController;
@@ -341,7 +341,7 @@
}
public void persistScaleAndTransitionTo(State state) {
- mFullScreenMagnificationController.persistScale();
+ mFullScreenMagnificationController.persistScale(mDisplayId);
clear();
transitionTo(state);
}
@@ -945,7 +945,7 @@
if (DEBUG_DETECTING) Slog.i(mLogTag, "zoomOn(" + centerX + ", " + centerY + ")");
final float scale = MathUtils.constrain(
- mFullScreenMagnificationController.getPersistedScale(),
+ mFullScreenMagnificationController.getPersistedScale(mDisplayId),
MIN_SCALE, MAX_SCALE);
mFullScreenMagnificationController.setScaleAndCenter(mDisplayId,
scale, centerX, centerY,
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 5a6836c..3708c7a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -23,11 +23,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
@@ -75,12 +77,15 @@
private final SparseArray<DisableMagnificationCallback>
mMagnificationEndRunnableSparseArray = new SparseArray();
+ private final MagnificationScaleProvider mScaleProvider;
private FullScreenMagnificationController mFullScreenMagnificationController;
private WindowMagnificationManager mWindowMagnificationMgr;
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@GuardedBy("mLock")
private int mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+ // Track the active user to reset the magnification and get the associated user settings.
+ private @UserIdInt int mUserId = UserHandle.USER_SYSTEM;
@GuardedBy("mLock")
private boolean mImeWindowVisible = false;
private long mWindowModeEnabledTime = 0;
@@ -98,17 +103,19 @@
}
public MagnificationController(AccessibilityManagerService ams, Object lock,
- Context context) {
+ Context context, MagnificationScaleProvider scaleProvider) {
mAms = ams;
mLock = lock;
mContext = context;
+ mScaleProvider = scaleProvider;
}
@VisibleForTesting
public MagnificationController(AccessibilityManagerService ams, Object lock,
Context context, FullScreenMagnificationController fullScreenMagnificationController,
- WindowMagnificationManager windowMagnificationManager) {
- this(ams, lock, context);
+ WindowMagnificationManager windowMagnificationManager,
+ MagnificationScaleProvider scaleProvider) {
+ this(ams, lock, context, scaleProvider);
mFullScreenMagnificationController = fullScreenMagnificationController;
mWindowMagnificationMgr = windowMagnificationManager;
}
@@ -194,7 +201,7 @@
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
- final float scale = windowMagnificationMgr.getPersistedScale();
+ final float scale = mScaleProvider.getScale(displayId);
final DisableMagnificationCallback animationEndCallback =
new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
scale, magnificationCenter);
@@ -313,13 +320,23 @@
* @param userId the currently active user ID
*/
public void updateUserIdIfNeeded(int userId) {
+ if (mUserId == userId) {
+ return;
+ }
+ mUserId = userId;
+ final FullScreenMagnificationController fullMagnificationController;
+ final WindowMagnificationManager windowMagnificationManager;
synchronized (mLock) {
- if (mFullScreenMagnificationController != null) {
- mFullScreenMagnificationController.setUserId(userId);
- }
- if (mWindowMagnificationMgr != null) {
- mWindowMagnificationMgr.setUserId(userId);
- }
+ fullMagnificationController = mFullScreenMagnificationController;
+ windowMagnificationManager = mWindowMagnificationMgr;
+ }
+
+ mScaleProvider.onUserChanged(userId);
+ if (fullMagnificationController != null) {
+ fullMagnificationController.resetAllIfNeeded(false);
+ }
+ if (windowMagnificationManager != null) {
+ windowMagnificationManager.disableAllWindowMagnifiers();
}
}
@@ -337,6 +354,14 @@
mWindowMagnificationMgr.onDisplayRemoved(displayId);
}
}
+ mScaleProvider.onDisplayRemoved(displayId);
+ }
+
+ /**
+ * Called when the given user is removed.
+ */
+ public void onUserRemoved(int userId) {
+ mScaleProvider.onUserRemoved(userId);
}
public void setMagnificationCapabilities(int capabilities) {
@@ -378,8 +403,7 @@
synchronized (mLock) {
if (mFullScreenMagnificationController == null) {
mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
- mAms, mLock, this);
- mFullScreenMagnificationController.setUserId(mAms.getCurrentUserIdLocked());
+ mAms, mLock, this, mScaleProvider);
}
}
return mFullScreenMagnificationController;
@@ -404,7 +428,8 @@
synchronized (mLock) {
if (mWindowMagnificationMgr == null) {
mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
- mAms.getCurrentUserIdLocked(), this, mAms.getTraceManager());
+ mUserId, this, mAms.getTraceManager(),
+ mScaleProvider);
}
return mWindowMagnificationMgr;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationScaleProvider.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationScaleProvider.java
new file mode 100644
index 0000000..8e1aa38
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationScaleProvider.java
@@ -0,0 +1,139 @@
+/*
+ * 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.accessibility.magnification;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.MathUtils;
+import android.util.SparseArray;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+
+/**
+ * Supplies setter/getter of the magnification scale for the given display. Only the value of the
+ * default play is persisted. It also constraints the range of applied magnification scale between
+ * [MIN_SCALE, MAX_SCALE] which is consistent with the range provided by
+ * {@code AccessibilityService.MagnificationController#setScale()}.
+ */
+public class MagnificationScaleProvider {
+
+ @VisibleForTesting
+ protected static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f;
+ public static final float MIN_SCALE = 1.0f;
+ public static final float MAX_SCALE = 8.0f;
+
+ private final Context mContext;
+ // Stores the scale for non-default displays.
+ @GuardedBy("mLock")
+ private final SparseArray<SparseArray<Float>> mUsersScales = new SparseArray();
+ private int mCurrentUserId = UserHandle.USER_SYSTEM;
+ private final Object mLock = new Object();
+
+ public MagnificationScaleProvider(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Stores the user settings scale associated to the given display. Only the scale of the
+ * default display is persistent.
+ *
+ * @param scale the magnification scale
+ * @param displayId the id of the display
+ */
+ void putScale(float scale, int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ BackgroundThread.getHandler().post(
+ () -> Settings.Secure.putFloatForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale,
+ mCurrentUserId));
+ } else {
+ synchronized (mLock) {
+ getScalesWithCurrentUser().put(displayId, scale);
+ }
+ }
+ }
+
+ /**
+ * Gets the user settings scale with the given display.
+ *
+ * @param displayId the id of the display
+ * @return the magnification scale.
+ */
+ float getScale(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ return Settings.Secure.getFloatForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+ DEFAULT_MAGNIFICATION_SCALE, mCurrentUserId);
+ } else {
+ synchronized (mLock) {
+ return getScalesWithCurrentUser().get(displayId, DEFAULT_MAGNIFICATION_SCALE);
+ }
+ }
+ }
+
+
+ @GuardedBy("mLock")
+ private SparseArray<Float> getScalesWithCurrentUser() {
+ SparseArray<Float> scales = mUsersScales.get(mCurrentUserId);
+ if (scales == null) {
+ scales = new SparseArray<>();
+ mUsersScales.put(mCurrentUserId, scales);
+ }
+
+ return scales;
+ }
+
+ void onUserChanged(int userId) {
+ synchronized (mLock) {
+ mCurrentUserId = userId;
+ }
+ }
+
+ void onUserRemoved(int userId) {
+ synchronized (mLock) {
+ mUsersScales.remove(userId);
+ }
+ }
+
+ void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ final int userCounts = mUsersScales.size();
+ for (int i = userCounts - 1; i >= 0; i--) {
+ mUsersScales.get(i).remove(displayId);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ synchronized (mLock) {
+ return "MagnificationScaleProvider{"
+ + "mCurrentUserId=" + mCurrentUserId
+ + "Scale on the default display=" + getScale(Display.DEFAULT_DISPLAY)
+ + "Scales on non-default displays=" + getScalesWithCurrentUser()
+ + '}';
+ }
+ }
+
+ static float constrainScale(float scale) {
+ return MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index bc61284..7d8f545 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -69,7 +69,7 @@
//Ensure the range has consistency with FullScreenMagnificationGestureHandler.
private static final float MIN_SCALE = 2.0f;
- private static final float MAX_SCALE = WindowMagnificationManager.MAX_SCALE;
+ private static final float MAX_SCALE = MagnificationScaleProvider.MAX_SCALE;
private final WindowMagnificationManager mWindowMagnificationMgr;
@VisibleForTesting
@@ -177,8 +177,7 @@
}
final float scale = MathUtils.constrain(
- mWindowMagnificationMgr.getPersistedScale(),
- MIN_SCALE, MAX_SCALE);
+ mWindowMagnificationMgr.getPersistedScale(mDisplayId), MIN_SCALE, MAX_SCALE);
mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 7a111d8..ce7ba75 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -29,8 +29,6 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.provider.Settings;
-import android.util.MathUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.view.MotionEvent;
@@ -40,7 +38,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -49,6 +46,8 @@
* A class to manipulate window magnification through {@link WindowMagnificationConnectionWrapper}
* create by {@link #setConnection(IWindowMagnificationConnection)}. To set the connection with
* SysUI, call {@code StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)}.
+ * The applied magnification scale is constrained by
+ * {@link MagnificationScaleProvider#constrainScale(float)}
*/
public class WindowMagnificationManager implements
PanningScalingHandler.MagnificationDelegate {
@@ -57,10 +56,6 @@
private static final String TAG = "WindowMagnificationMgr";
- //Ensure the range has consistency with full screen.
- static final float MAX_SCALE = FullScreenMagnificationController.MAX_SCALE;
- static final float MIN_SCALE = FullScreenMagnificationController.MIN_SCALE;
-
private final Object mLock = new Object();
private final Context mContext;
@VisibleForTesting
@@ -71,7 +66,6 @@
private ConnectionCallback mConnectionCallback;
@GuardedBy("mLock")
private SparseArray<WindowMagnifier> mWindowMagnifiers = new SparseArray<>();
- private int mUserId;
private boolean mReceiverRegistered = false;
@VisibleForTesting
@@ -116,13 +110,14 @@
private final Callback mCallback;
private final AccessibilityTraceManager mTrace;
+ private final MagnificationScaleProvider mScaleProvider;
public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback,
- AccessibilityTraceManager trace) {
+ AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) {
mContext = context;
- mUserId = userId;
mCallback = callback;
mTrace = trace;
+ mScaleProvider = scaleProvider;
}
/**
@@ -159,15 +154,6 @@
}
/**
- * Sets the currently active user ID.
- *
- * @param userId the currently active user ID
- */
- public void setUserId(int userId) {
- mUserId = userId;
- }
-
- /**
* @return {@code true} if {@link IWindowMagnificationConnection} is available
*/
public boolean isConnected() {
@@ -219,13 +205,18 @@
return true;
}
- @GuardedBy("mLock")
- private void disableAllWindowMagnifiers() {
- for (int i = 0; i < mWindowMagnifiers.size(); i++) {
- final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i);
- magnifier.disableWindowMagnificationInternal(null);
+ /**
+ * Disables window magnifier on all displays without animation.
+ */
+ void disableAllWindowMagnifiers() {
+ synchronized (mLock) {
+ for (int i = 0; i < mWindowMagnifiers.size(); i++) {
+ final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i);
+ magnifier.disableWindowMagnificationInternal(null);
+ }
+ mWindowMagnifiers.clear();
}
- mWindowMagnifiers.clear();
+
}
private void resetWindowMagnifiers() {
@@ -378,29 +369,24 @@
}
/**
- * Retrieves a previously persisted magnification scale from the current
- * user's settings.
+ * Retrieves a previously magnification scale from the current
+ * user's settings. Only the value of the default display is persisted.
*
- * @return the previously persisted magnification scale, or the default
+ * @return the previously magnification scale, or the default
* scale if none is available
*/
- float getPersistedScale() {
- return Settings.Secure.getFloatForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
- MIN_SCALE, mUserId);
+ float getPersistedScale(int displayId) {
+ return mScaleProvider.getScale(displayId);
}
/**
- * Persists the default display magnification scale to the current user's settings.
+ * Persists the default display magnification scale to the current user's settings. Only the
+ * value of the default display is persisted in user's settings.
*/
void persistScale(int displayId) {
-
float scale = getScale(displayId);
if (scale != 1.0f) {
- BackgroundThread.getHandler().post(() -> {
- Settings.Secure.putFloatForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale, mUserId);
- });
+ mScaleProvider.putScale(scale, displayId);
}
}
@@ -511,7 +497,7 @@
*
* @param displayId The logical display id.
*/
- void onDisplayRemoved(int displayId) {
+ public void onDisplayRemoved(int displayId) {
disableWindowMagnification(displayId, true);
}
@@ -613,7 +599,7 @@
private static class WindowMagnifier {
private final int mDisplayId;
- private float mScale = MIN_SCALE;
+ private float mScale = MagnificationScaleProvider.MIN_SCALE;
private boolean mEnabled;
private final WindowMagnificationManager mWindowMagnificationManager;
@@ -633,7 +619,7 @@
if (mEnabled) {
return false;
}
- final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+ final float normScale = MagnificationScaleProvider.constrainScale(scale);
if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
centerX, centerY, animationCallback)) {
mScale = normScale;
@@ -664,7 +650,7 @@
if (!mEnabled) {
return;
}
- final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+ final float normScale = MagnificationScaleProvider.constrainScale(scale);
if (Float.compare(mScale, normScale) != 0
&& mWindowMagnificationManager.setScaleInternal(mDisplayId, scale)) {
mScale = normScale;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
index 1fdd908..ecf999e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
@@ -160,7 +160,7 @@
intentFilter.addAction(ACTION_DISMISS);
intentFilter.addAction(ACTION_TURN_ON_IN_SETTINGS);
mContext.registerReceiver(mNotificationActionReceiver, intentFilter,
- Manifest.permission.MANAGE_ACCESSIBILITY, null);
+ Manifest.permission.MANAGE_ACCESSIBILITY, null, Context.RECEIVER_EXPORTED);
}
private void launchMagnificationSettings() {
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 4946ad4..1af8ad3 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -187,7 +187,7 @@
@NonNull IPredictionCallback callback) {
final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
if (sessionInfo == null) return;
- final boolean serviceExists = resolveService(sessionId, false,
+ final boolean serviceExists = resolveService(sessionId, true,
sessionInfo.mUsesPeopleService,
s -> s.registerPredictionUpdates(sessionId, callback));
if (serviceExists) {
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index c32543a..78d9095 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -147,7 +147,7 @@
OnCrossProfileWidgetProvidersChangeListener {
private static final String TAG = "AppWidgetServiceImpl";
- private static boolean DEBUG = false;
+ private static final boolean DEBUG = false;
private static final String OLD_KEYGUARD_HOST_PACKAGE = "android";
private static final String NEW_KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
new file mode 100644
index 0000000..2e8fb47
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -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.server.companion;
+
+import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static com.android.internal.util.CollectionUtils.filter;
+import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
+import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
+import static com.android.server.companion.CompanionDeviceManagerService.getCallingUserId;
+
+import static java.util.Collections.unmodifiableMap;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
+import android.companion.CompanionDeviceManager;
+import android.companion.ICompanionDeviceDiscoveryService;
+import android.companion.IFindDeviceCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.Signature;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.PackageUtils;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.infra.PerUser;
+import com.android.internal.infra.ServiceConnector;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.FgThread;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+class AssociationRequestsProcessor {
+ private static final String TAG = LOG_TAG + ".AssociationRequestsProcessor";
+
+ private static final Map<String, String> DEVICE_PROFILE_TO_PERMISSION;
+ static {
+ final Map<String, String> map = new ArrayMap<>();
+ map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH);
+ map.put(DEVICE_PROFILE_APP_STREAMING,
+ Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING);
+
+ DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
+ }
+
+ private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative(
+ CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
+ ".CompanionDeviceDiscoveryService");
+
+ private static final int ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW = 5;
+ private static final long ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS = 60 * 60 * 1000; // 60 min;
+
+ private final Context mContext;
+ private final CompanionDeviceManagerService mService;
+
+ private AssociationRequest mRequest;
+ private IFindDeviceCallback mFindDeviceCallback;
+ private String mCallingPackage;
+ private AndroidFuture<?> mOngoingDeviceDiscovery;
+
+ private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
+
+ AssociationRequestsProcessor(CompanionDeviceManagerService service) {
+ mContext = service.getContext();
+ mService = service;
+
+ final Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
+ mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
+ @Override
+ protected ServiceConnector<ICompanionDeviceDiscoveryService> create(int userId) {
+ return new ServiceConnector.Impl<>(
+ mContext,
+ serviceIntent, 0/* bindingFlags */, userId,
+ ICompanionDeviceDiscoveryService.Stub::asInterface);
+ }
+ };
+ }
+
+ void process(AssociationRequest request, IFindDeviceCallback callback, String callingPackage)
+ throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "process(request=" + request + ", from=" + callingPackage + ")");
+ }
+
+ checkNotNull(request, "Request cannot be null");
+ checkNotNull(callback, "Callback cannot be null");
+ mService.checkCallerIsSystemOr(callingPackage);
+ int userId = getCallingUserId();
+ mService.checkUsesFeature(callingPackage, userId);
+ final String deviceProfile = request.getDeviceProfile();
+ validateDeviceProfileAndCheckPermission(deviceProfile);
+
+ mFindDeviceCallback = callback;
+ mRequest = request;
+ mCallingPackage = callingPackage;
+ request.setCallingPackage(callingPackage);
+
+ if (mayAssociateWithoutPrompt(callingPackage, userId)) {
+ Slog.i(TAG, "setSkipPrompt(true)");
+ request.setSkipPrompt(true);
+ }
+ callback.asBinder().linkToDeath(mBinderDeathRecipient /* recipient */, 0);
+
+ mOngoingDeviceDiscovery = getDeviceProfilePermissionDescription(deviceProfile)
+ .thenComposeAsync(description -> {
+ if (DEBUG) {
+ Slog.d(TAG, "fetchProfileDescription done: " + description);
+ }
+
+ request.setDeviceProfilePrivilegesDescription(description);
+
+ return mServiceConnectors.forUser(userId).postAsync(service -> {
+ if (DEBUG) {
+ Slog.d(TAG, "Connected to CDM service -> "
+ + "Starting discovery for " + request);
+ }
+
+ AndroidFuture<String> future = new AndroidFuture<>();
+ service.startDiscovery(request, callingPackage, callback, future);
+ return future;
+ }).cancelTimeout();
+
+ }, FgThread.getExecutor()).whenComplete(uncheckExceptions((deviceAddress, err) -> {
+ if (err == null) {
+ mService.createAssociationInternal(
+ userId, deviceAddress, callingPackage, deviceProfile);
+ } else {
+ Slog.e(TAG, "Failed to discover device(s)", err);
+ callback.onFailure("No devices found: " + err.getMessage());
+ }
+ cleanup();
+ }));
+ }
+
+ void stopScan(AssociationRequest request, IFindDeviceCallback callback, String callingPackage) {
+ if (DEBUG) {
+ Slog.d(TAG, "stopScan(request = " + request + ")");
+ }
+ if (Objects.equals(request, mRequest)
+ && Objects.equals(callback, mFindDeviceCallback)
+ && Objects.equals(callingPackage, mCallingPackage)) {
+ cleanup();
+ }
+ }
+
+ private void validateDeviceProfileAndCheckPermission(@Nullable String deviceProfile) {
+ // Device profile can be null.
+ if (deviceProfile == null) return;
+
+ if (DEVICE_PROFILE_APP_STREAMING.equals(deviceProfile)) {
+ // TODO: remove, when properly supporting this profile.
+ throw new UnsupportedOperationException(
+ "DEVICE_PROFILE_APP_STREAMING is not fully supported yet.");
+ }
+
+ if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile)) {
+ throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile);
+ }
+
+ final String permission = DEVICE_PROFILE_TO_PERMISSION.get(deviceProfile);
+ if (mContext.checkCallingOrSelfPermission(permission) != PERMISSION_GRANTED) {
+ throw new SecurityException("Application must hold " + permission + " to associate "
+ + "with a device with " + deviceProfile + " profile.");
+ }
+ }
+
+ private void cleanup() {
+ if (DEBUG) {
+ Slog.d(TAG, "cleanup(); discovery = "
+ + mOngoingDeviceDiscovery + ", request = " + mRequest);
+ }
+ synchronized (mService.mLock) {
+ AndroidFuture<?> ongoingDeviceDiscovery = mOngoingDeviceDiscovery;
+ if (ongoingDeviceDiscovery != null && !ongoingDeviceDiscovery.isDone()) {
+ ongoingDeviceDiscovery.cancel(true);
+ }
+ if (mFindDeviceCallback != null) {
+ mFindDeviceCallback.asBinder().unlinkToDeath(mBinderDeathRecipient, 0);
+ mFindDeviceCallback = null;
+ }
+ mRequest = null;
+ mCallingPackage = null;
+ }
+ }
+
+ private boolean mayAssociateWithoutPrompt(String packageName, int userId) {
+ String[] sameOemPackages = mContext.getResources()
+ .getStringArray(com.android.internal.R.array.config_companionDevicePackages);
+ if (!ArrayUtils.contains(sameOemPackages, packageName)) {
+ Slog.w(TAG, packageName
+ + " can not silently create associations due to no package found."
+ + " Packages from OEM: " + Arrays.toString(sameOemPackages)
+ );
+ return false;
+ }
+
+ // Throttle frequent associations
+ long now = System.currentTimeMillis();
+ Set<AssociationInfo> recentAssociations = filter(
+ mService.getAllAssociations(userId, packageName),
+ a -> now - a.getTimeApprovedMs() < ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS);
+
+ if (recentAssociations.size() >= ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW) {
+ Slog.w(TAG, "Too many associations. " + packageName
+ + " already associated " + recentAssociations.size()
+ + " devices within the last " + ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS
+ + "ms: " + recentAssociations);
+ return false;
+ }
+ String[] sameOemCerts = mContext.getResources()
+ .getStringArray(com.android.internal.R.array.config_companionDeviceCerts);
+
+ Signature[] signatures = mService.mPackageManagerInternal
+ .getPackage(packageName).getSigningDetails().getSignatures();
+ String[] apkCerts = PackageUtils.computeSignaturesSha256Digests(signatures);
+
+ Set<String> sameOemPackageCerts =
+ getSameOemPackageCerts(packageName, sameOemPackages, sameOemCerts);
+
+ for (String cert : apkCerts) {
+ if (sameOemPackageCerts.contains(cert)) {
+ return true;
+ }
+ }
+
+ Slog.w(TAG, packageName
+ + " can not silently create associations. " + packageName
+ + " has SHA256 certs from APK: " + Arrays.toString(apkCerts)
+ + " and from OEM: " + Arrays.toString(sameOemCerts)
+ );
+
+ return false;
+ }
+
+ @NonNull
+ private AndroidFuture<String> getDeviceProfilePermissionDescription(
+ @Nullable String deviceProfile) {
+ if (deviceProfile == null) {
+ return AndroidFuture.completedFuture(null);
+ }
+
+ final AndroidFuture<String> result = new AndroidFuture<>();
+ mService.mPermissionControllerManager.getPrivilegesDescriptionStringForProfile(
+ deviceProfile, FgThread.getExecutor(), desc -> {
+ try {
+ result.complete(String.valueOf(desc));
+ } catch (Exception e) {
+ result.completeExceptionally(e);
+ }
+ });
+ return result;
+ }
+
+
+ void dump(@NonNull PrintWriter pw) {
+ pw.append("Discovery Service State:").append('\n');
+ for (int i = 0, size = mServiceConnectors.size(); i < size; i++) {
+ int userId = mServiceConnectors.keyAt(i);
+ pw.append(" ")
+ .append("u").append(Integer.toString(userId)).append(": ")
+ .append(Objects.toString(mServiceConnectors.valueAt(i)))
+ .append('\n');
+ }
+ }
+
+ private final IBinder.DeathRecipient mBinderDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ if (DEBUG) {
+ Slog.d(TAG, "binderDied()");
+ }
+ mService.mMainHandler.post(AssociationRequestsProcessor.this::cleanup);
+ }
+ };
+
+ private static Set<String> getSameOemPackageCerts(
+ String packageName, String[] oemPackages, String[] sameOemCerts) {
+ Set<String> sameOemPackageCerts = new HashSet<>();
+
+ // Assume OEM may enter same package name in the parallel string array with
+ // multiple APK certs corresponding to it
+ for (int i = 0; i < oemPackages.length; i++) {
+ if (oemPackages[i].equals(packageName)) {
+ sameOemPackageCerts.add(sameOemCerts[i].replaceAll(":", ""));
+ }
+ }
+
+ return sameOemPackageCerts;
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index a48172b..cdce3e6 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -19,8 +19,6 @@
import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
-import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
-import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static android.companion.DeviceId.TYPE_MAC_ADDRESS;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -31,7 +29,6 @@
import static com.android.internal.util.CollectionUtils.find;
import static com.android.internal.util.CollectionUtils.forEach;
import static com.android.internal.util.CollectionUtils.map;
-import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkState;
@@ -39,13 +36,10 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
import static java.util.Collections.emptySet;
-import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.MINUTES;
-import android.Manifest;
-import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -62,12 +56,10 @@
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
-import android.companion.Association;
+import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
-import android.companion.CompanionDeviceManager;
import android.companion.DeviceId;
import android.companion.DeviceNotAssociatedException;
-import android.companion.ICompanionDeviceDiscoveryService;
import android.companion.ICompanionDeviceManager;
import android.companion.IFindDeviceCallback;
import android.content.BroadcastReceiver;
@@ -81,14 +73,11 @@
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.net.NetworkPolicyManager;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
-import android.os.IBinder;
-import android.os.IInterface;
import android.os.Parcel;
import android.os.PowerWhitelistManager;
import android.os.Process;
@@ -103,7 +92,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.ExceptionUtils;
-import android.util.PackageUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -111,9 +99,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
-import com.android.internal.infra.AndroidFuture;
-import com.android.internal.infra.PerUser;
-import com.android.internal.infra.ServiceConnector;
import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
@@ -146,27 +131,13 @@
/** @hide */
@SuppressLint("LongLogTag")
-public class CompanionDeviceManagerService extends SystemService implements Binder.DeathRecipient {
+public class CompanionDeviceManagerService extends SystemService {
static final String LOG_TAG = "CompanionDeviceManagerService";
static final boolean DEBUG = false;
- private static final Map<String, String> DEVICE_PROFILE_TO_PERMISSION;
- static {
- final Map<String, String> map = new ArrayMap<>();
- map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH);
- map.put(DEVICE_PROFILE_APP_STREAMING,
- Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING);
-
- DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
- }
-
/** Range of Association IDs allocated for a user.*/
static final int ASSOCIATIONS_IDS_PER_USER_RANGE = 100000;
- private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative(
- CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
- ".CompanionDeviceDiscoveryService");
-
private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000;
private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000;
@@ -177,9 +148,6 @@
private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
- private static final int ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW = 5;
- private static final long ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS = 60 * 60 * 1000; // 60 min;
-
private static DateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
static {
sDateFormat.setTimeZone(TimeZone.getDefault());
@@ -188,19 +156,15 @@
private final CompanionDeviceManagerImpl mImpl;
// Persistent data store for all Associations.
private final PersistentDataStore mPersistentDataStore;
+ private final AssociationRequestsProcessor mAssociationRequestsProcessor;
private PowerWhitelistManager mPowerWhitelistManager;
- private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
private IAppOpsService mAppOpsManager;
private RoleManager mRoleManager;
private BluetoothAdapter mBluetoothAdapter;
private UserManager mUserManager;
- private IFindDeviceCallback mFindDeviceCallback;
private ScanCallback mBleScanCallback = new BleScanCallback();
- private AssociationRequest mRequest;
- private String mCallingPackage;
- private AndroidFuture<?> mOngoingDeviceDiscovery;
- private PermissionControllerManager mPermissionControllerManager;
+ PermissionControllerManager mPermissionControllerManager;
private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener =
new BluetoothDeviceConnectedListener();
@@ -212,13 +176,13 @@
private ArrayMap<String, TriggerDeviceDisappearedRunnable> mTriggerDeviceDisappearedRunnables =
new ArrayMap<>();
- private final Object mLock = new Object();
- private final Handler mMainHandler = Handler.getMain();
+ final Object mLock = new Object();
+ final Handler mMainHandler = Handler.getMain();
private CompanionDevicePresenceController mCompanionDevicePresenceController;
/** Maps a {@link UserIdInt} to a set of associations for the user. */
@GuardedBy("mLock")
- private final SparseArray<Set<Association>> mCachedAssociations = new SparseArray<>();
+ private final SparseArray<Set<AssociationInfo>> mCachedAssociations = new SparseArray<>();
/**
* A structure that consist of two nested maps, and effectively maps (userId + packageName) to
* a list of IDs that have been previously assigned to associations for that package.
@@ -236,6 +200,7 @@
super(context);
mImpl = new CompanionDeviceManagerImpl();
mPersistentDataStore = new PersistentDataStore();
+ mAssociationRequestsProcessor = new AssociationRequestsProcessor(this);
mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class);
mRoleManager = context.getSystemService(RoleManager.class);
@@ -249,17 +214,6 @@
mUserManager = context.getSystemService(UserManager.class);
mCompanionDevicePresenceController = new CompanionDevicePresenceController();
- Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
- mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
- @Override
- protected ServiceConnector<ICompanionDeviceDiscoveryService> create(int userId) {
- return new ServiceConnector.Impl<>(
- getContext(),
- serviceIntent, 0/* bindingFlags */, userId,
- ICompanionDeviceDiscoveryService.Stub::asInterface);
- }
- };
-
registerPackageMonitor();
}
@@ -316,7 +270,7 @@
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
int userHandle = user.getUserIdentifier();
- Set<Association> associations = getAllAssociations(userHandle);
+ Set<AssociationInfo> associations = getAllAssociations(userHandle);
if (associations == null || associations.isEmpty()) {
return;
}
@@ -339,11 +293,11 @@
}
try {
- Set<Association> associations = getAllAssociations(userId);
+ Set<AssociationInfo> associations = getAllAssociations(userId);
if (associations == null) {
continue;
}
- for (Association a : associations) {
+ for (AssociationInfo a : associations) {
try {
int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
exemptFromAutoRevoke(a.getPackageName(), uid);
@@ -357,39 +311,6 @@
}
}
- @Override
- public void binderDied() {
- Slog.w(LOG_TAG, "binderDied()");
- mMainHandler.post(this::cleanup);
- }
-
- private void cleanup() {
- Slog.d(LOG_TAG, "cleanup(); discovery = "
- + mOngoingDeviceDiscovery + ", request = " + mRequest);
- synchronized (mLock) {
- AndroidFuture<?> ongoingDeviceDiscovery = mOngoingDeviceDiscovery;
- if (ongoingDeviceDiscovery != null && !ongoingDeviceDiscovery.isDone()) {
- ongoingDeviceDiscovery.cancel(true);
- }
- mFindDeviceCallback = unlinkToDeath(mFindDeviceCallback, this, 0);
- mRequest = null;
- mCallingPackage = null;
- }
- }
-
- /**
- * Usage: {@code a = unlinkToDeath(a, deathRecipient, flags); }
- */
- @Nullable
- @CheckResult
- private static <T extends IInterface> T unlinkToDeath(T iinterface,
- IBinder.DeathRecipient deathRecipient, int flags) {
- if (iinterface != null) {
- iinterface.asBinder().unlinkToDeath(deathRecipient, flags);
- }
- return null;
- }
-
class CompanionDeviceManagerImpl extends ICompanionDeviceManager.Stub {
@Override
@@ -410,61 +331,15 @@
String callingPackage) throws RemoteException {
Slog.i(LOG_TAG, "associate(request = " + request + ", callback = " + callback
+ ", callingPackage = " + callingPackage + ")");
- checkNotNull(request, "Request cannot be null");
- checkNotNull(callback, "Callback cannot be null");
- checkCallerIsSystemOr(callingPackage);
- int userId = getCallingUserId();
- checkUsesFeature(callingPackage, userId);
- final String deviceProfile = request.getDeviceProfile();
- validateDeviceProfileAndCheckPermission(deviceProfile);
-
- mFindDeviceCallback = callback;
- mRequest = request;
- mCallingPackage = callingPackage;
- request.setCallingPackage(callingPackage);
-
- if (mayAssociateWithoutPrompt(callingPackage, userId)) {
- Slog.i(LOG_TAG, "setSkipPrompt(true)");
- request.setSkipPrompt(true);
- }
- callback.asBinder().linkToDeath(CompanionDeviceManagerService.this /* recipient */, 0);
-
- mOngoingDeviceDiscovery = getDeviceProfilePermissionDescription(deviceProfile)
- .thenComposeAsync(description -> {
- Slog.d(LOG_TAG, "fetchProfileDescription done: " + description);
-
- request.setDeviceProfilePrivilegesDescription(description);
-
- return mServiceConnectors.forUser(userId).postAsync(service -> {
- Slog.d(LOG_TAG, "Connected to CDM service; starting discovery for " + request);
-
- AndroidFuture<String> future = new AndroidFuture<>();
- service.startDiscovery(request, callingPackage, callback, future);
- return future;
- }).cancelTimeout();
-
- }, FgThread.getExecutor()).whenComplete(uncheckExceptions((deviceAddress, err) -> {
- if (err == null) {
- createAssociationInternal(
- userId, deviceAddress, callingPackage, deviceProfile);
- } else {
- Slog.e(LOG_TAG, "Failed to discover device(s)", err);
- callback.onFailure("No devices found: " + err.getMessage());
- }
- cleanup();
- }));
+ mAssociationRequestsProcessor.process(request, callback, callingPackage);
}
@Override
public void stopScan(AssociationRequest request,
IFindDeviceCallback callback,
String callingPackage) {
- Slog.d(LOG_TAG, "stopScan(request = " + request + ")");
- if (Objects.equals(request, mRequest)
- && Objects.equals(callback, mFindDeviceCallback)
- && Objects.equals(callingPackage, mCallingPackage)) {
- cleanup();
- }
+ Slog.i(LOG_TAG, "stopScan(request = " + request + ")");
+ mAssociationRequestsProcessor.stopScan(request, callback, callingPackage);
}
@Override
@@ -480,7 +355,7 @@
}
@Override
- public List<Association> getAssociationsForUser(int userId) {
+ public List<AssociationInfo> getAssociationsForUser(int userId) {
if (!callerCanManageCompanionDevices()) {
throw new SecurityException("Caller must hold "
+ android.Manifest.permission.MANAGE_COMPANION_DEVICES);
@@ -505,44 +380,6 @@
== PERMISSION_GRANTED;
}
- private void checkCallerIsSystemOr(String pkg) throws RemoteException {
- checkCallerIsSystemOr(pkg, getCallingUserId());
- }
-
- private void checkCallerIsSystemOr(String pkg, int userId) throws RemoteException {
- if (isCallerSystem()) {
- return;
- }
-
- checkArgument(getCallingUserId() == userId,
- "Must be called by either same user or system");
- int callingUid = Binder.getCallingUid();
- if (mAppOpsManager.checkPackage(callingUid, pkg) != AppOpsManager.MODE_ALLOWED) {
- throw new SecurityException(pkg + " doesn't belong to uid " + callingUid);
- }
- }
-
- private void validateDeviceProfileAndCheckPermission(@Nullable String deviceProfile) {
- // Device profile can be null.
- if (deviceProfile == null) return;
-
- if (DEVICE_PROFILE_APP_STREAMING.equals(deviceProfile)) {
- // TODO: remove, when properly supporting this profile.
- throw new UnsupportedOperationException(
- "DEVICE_PROFILE_APP_STREAMING is not fully supported yet.");
- }
-
- if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile)) {
- throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile);
- }
-
- final String permission = DEVICE_PROFILE_TO_PERMISSION.get(deviceProfile);
- if (getContext().checkCallingOrSelfPermission(permission) != PERMISSION_GRANTED) {
- throw new SecurityException("Application must hold " + permission + " to associate "
- + "with a device with " + deviceProfile + " profile.");
- }
- }
-
@Override
public PendingIntent requestNotificationAccess(ComponentName component)
throws RemoteException {
@@ -630,7 +467,7 @@
checkCallerIsSystemOr(packageName);
int userId = getCallingUserId();
- Set<Association> deviceAssociations = filter(
+ Set<AssociationInfo> deviceAssociations = filter(
getAllAssociations(userId, packageName),
association -> deviceAddress.equals(association.getDeviceMacAddress()));
@@ -674,23 +511,6 @@
checkUsesFeature(callingPackage, userId);
}
- private void checkUsesFeature(String pkg, int userId) {
- if (isCallerSystem()) {
- // Drop the requirement for calls from system process
- return;
- }
-
- FeatureInfo[] reqFeatures = getPackageInfo(pkg, userId).reqFeatures;
- String requiredFeature = PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
- int numFeatures = ArrayUtils.size(reqFeatures);
- for (int i = 0; i < numFeatures; i++) {
- if (requiredFeature.equals(reqFeatures[i].name)) return;
- }
- throw new IllegalStateException("Must declare uses-feature "
- + requiredFeature
- + " in manifest to use this API");
- }
-
@Override
public boolean canPairWithoutPrompt(
String packageName, String deviceMacAddress, int userId) {
@@ -740,14 +560,7 @@
.append(sDateFormat.format(time)).append('\n');
}
- fout.append("Discovery Service State:").append('\n');
- for (int i = 0, size = mServiceConnectors.size(); i < size; i++) {
- int userId = mServiceConnectors.keyAt(i);
- fout.append(" ")
- .append("u").append(Integer.toString(userId)).append(": ")
- .append(Objects.toString(mServiceConnectors.valueAt(i)))
- .append('\n');
- }
+ mAssociationRequestsProcessor.dump(fout);
fout.append("Device Listener Services State:").append('\n');
for (int i = 0, size = mCompanionDevicePresenceController.mBoundServices.size();
@@ -762,7 +575,24 @@
}
}
- private static int getCallingUserId() {
+ void checkCallerIsSystemOr(String pkg) throws RemoteException {
+ checkCallerIsSystemOr(pkg, getCallingUserId());
+ }
+
+ private void checkCallerIsSystemOr(String pkg, int userId) throws RemoteException {
+ if (isCallerSystem()) {
+ return;
+ }
+
+ checkArgument(getCallingUserId() == userId,
+ "Must be called by either same user or system");
+ int callingUid = Binder.getCallingUid();
+ if (mAppOpsManager.checkPackage(callingUid, pkg) != AppOpsManager.MODE_ALLOWED) {
+ throw new SecurityException(pkg + " doesn't belong to uid " + callingUid);
+ }
+ }
+
+ static int getCallingUserId() {
return UserHandle.getUserId(Binder.getCallingUid());
}
@@ -770,9 +600,26 @@
return Binder.getCallingUid() == Process.SYSTEM_UID;
}
+ void checkUsesFeature(String pkg, int userId) {
+ if (isCallerSystem()) {
+ // Drop the requirement for calls from system process
+ return;
+ }
+
+ FeatureInfo[] reqFeatures = getPackageInfo(pkg, userId).reqFeatures;
+ String requiredFeature = PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
+ int numFeatures = ArrayUtils.size(reqFeatures);
+ for (int i = 0; i < numFeatures; i++) {
+ if (requiredFeature.equals(reqFeatures[i].name)) return;
+ }
+ throw new IllegalStateException("Must declare uses-feature "
+ + requiredFeature
+ + " in manifest to use this API");
+ }
+
void createAssociationInternal(
int userId, String deviceMacAddress, String packageName, String deviceProfile) {
- final Association association = new Association(
+ final AssociationInfo association = new AssociationInfo(
getNewAssociationIdForPackage(userId, packageName),
userId,
packageName,
@@ -801,7 +648,7 @@
// First: collect all IDs currently in use for this user's Associations.
final SparseBooleanArray usedIds = new SparseBooleanArray();
- for (Association it : getAllAssociations(userId)) {
+ for (AssociationInfo it : getAllAssociations(userId)) {
usedIds.put(it.getAssociationId(), true);
}
@@ -851,7 +698,7 @@
}
}
- void onAssociationPreRemove(Association association) {
+ void onAssociationPreRemove(AssociationInfo association) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.unbindDevicePresenceListener(
association.getPackageName(), association.getUserId());
@@ -859,7 +706,7 @@
String deviceProfile = association.getDeviceProfile();
if (deviceProfile != null) {
- Association otherAssociationWithDeviceProfile = find(
+ AssociationInfo otherAssociationWithDeviceProfile = find(
getAllAssociations(association.getUserId()),
a -> !a.equals(association) && deviceProfile.equals(a.getDeviceProfile()));
if (otherAssociationWithDeviceProfile != null) {
@@ -890,7 +737,7 @@
}
}
- private void updateSpecialAccessPermissionForAssociatedPackage(Association association) {
+ private void updateSpecialAccessPermissionForAssociatedPackage(AssociationInfo association) {
PackageInfo packageInfo = getPackageInfo(
association.getPackageName(),
association.getUserId());
@@ -904,7 +751,7 @@
}
private void updateSpecialAccessPermissionAsSystem(
- Association association, PackageInfo packageInfo) {
+ AssociationInfo association, PackageInfo packageInfo) {
if (containsEither(packageInfo.requestedPermissions,
android.Manifest.permission.RUN_IN_BACKGROUND,
android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
@@ -950,72 +797,6 @@
}
}
- private Set<String> getSameOemPackageCerts(
- String packageName, String[] oemPackages, String[] sameOemCerts) {
- Set<String> sameOemPackageCerts = new HashSet<>();
-
- // Assume OEM may enter same package name in the parallel string array with
- // multiple APK certs corresponding to it
- for (int i = 0; i < oemPackages.length; i++) {
- if (oemPackages[i].equals(packageName)) {
- sameOemPackageCerts.add(sameOemCerts[i].replaceAll(":", ""));
- }
- }
-
- return sameOemPackageCerts;
- }
-
- boolean mayAssociateWithoutPrompt(String packageName, int userId) {
- String[] sameOemPackages = getContext()
- .getResources()
- .getStringArray(com.android.internal.R.array.config_companionDevicePackages);
- if (!ArrayUtils.contains(sameOemPackages, packageName)) {
- Slog.w(LOG_TAG, packageName
- + " can not silently create associations due to no package found."
- + " Packages from OEM: " + Arrays.toString(sameOemPackages)
- );
- return false;
- }
-
- // Throttle frequent associations
- long now = System.currentTimeMillis();
- Set<Association> recentAssociations = filter(
- getAllAssociations(userId, packageName),
- a -> now - a.getTimeApprovedMs() < ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS);
-
- if (recentAssociations.size() >= ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW) {
- Slog.w(LOG_TAG, "Too many associations. " + packageName
- + " already associated " + recentAssociations.size()
- + " devices within the last " + ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS
- + "ms: " + recentAssociations);
- return false;
- }
- String[] sameOemCerts = getContext()
- .getResources()
- .getStringArray(com.android.internal.R.array.config_companionDeviceCerts);
-
- Signature[] signatures = mPackageManagerInternal
- .getPackage(packageName).getSigningDetails().getSignatures();
- String[] apkCerts = PackageUtils.computeSignaturesSha256Digests(signatures);
-
- Set<String> sameOemPackageCerts =
- getSameOemPackageCerts(packageName, sameOemPackages, sameOemCerts);
-
- for (String cert : apkCerts) {
- if (sameOemPackageCerts.contains(cert)) {
- return true;
- }
- }
-
- Slog.w(LOG_TAG, packageName
- + " can not silently create associations. " + packageName
- + " has SHA256 certs from APK: " + Arrays.toString(apkCerts)
- + " and from OEM: " + Arrays.toString(sameOemCerts)
- );
-
- return false;
- }
-
private static <T> boolean containsEither(T[] array, T a, T b) {
return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b);
}
@@ -1035,20 +816,20 @@
}, getContext(), packageName, userId).recycleOnUse());
}
- private void recordAssociation(Association association, int userId) {
+ private void recordAssociation(AssociationInfo association, int userId) {
Slog.i(LOG_TAG, "recordAssociation(" + association + ")");
updateAssociations(associations -> add(associations, association), userId);
}
- private void updateAssociations(Function<Set<Association>, Set<Association>> update,
+ private void updateAssociations(Function<Set<AssociationInfo>, Set<AssociationInfo>> update,
int userId) {
synchronized (mLock) {
if (DEBUG) Slog.d(LOG_TAG, "Updating Associations set...");
- final Set<Association> prevAssociations = getAllAssociations(userId);
+ final Set<AssociationInfo> prevAssociations = getAllAssociations(userId);
if (DEBUG) Slog.d(LOG_TAG, " > Before : " + prevAssociations + "...");
- final Set<Association> updatedAssociations = update.apply(
+ final Set<AssociationInfo> updatedAssociations = update.apply(
new ArraySet<>(prevAssociations));
if (DEBUG) Slog.d(LOG_TAG, " > After: " + updatedAssociations);
@@ -1065,9 +846,9 @@
}
}
- private void updateAtm(int userId, Set<Association> associations) {
+ private void updateAtm(int userId, Set<AssociationInfo> associations) {
final Set<Integer> companionAppUids = new ArraySet<>();
- for (Association association : associations) {
+ for (AssociationInfo association : associations) {
final int uid = mPackageManagerInternal.getPackageUid(association.getPackageName(),
0, userId);
if (uid >= 0) {
@@ -1083,7 +864,7 @@
}
}
- @NonNull Set<Association> getAllAssociations(int userId) {
+ @NonNull Set<AssociationInfo> getAllAssociations(int userId) {
synchronized (mLock) {
readPersistedStateForUserIfNeededLocked(userId);
// This returns non-null, because the readAssociationsInfoForUserIfNeededLocked() method
@@ -1098,7 +879,7 @@
Slog.i(LOG_TAG, "Reading state for user " + userId + " from the disk");
- final Set<Association> associations = new ArraySet<>();
+ final Set<AssociationInfo> associations = new ArraySet<>();
final Map<String, Set<Integer>> previouslyUsedIds = new ArrayMap<>();
mPersistentDataStore.readStateForUser(userId, associations, previouslyUsedIds);
@@ -1120,17 +901,17 @@
}
}
- private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
+ Set<AssociationInfo> getAllAssociations(int userId, @Nullable String packageFilter) {
return filter(
getAllAssociations(userId),
// Null filter == get all associations
a -> packageFilter == null || Objects.equals(packageFilter, a.getPackageName()));
}
- private Set<Association> getAllAssociations() {
+ private Set<AssociationInfo> getAllAssociations() {
final long identity = Binder.clearCallingIdentity();
try {
- ArraySet<Association> result = new ArraySet<>();
+ ArraySet<AssociationInfo> result = new ArraySet<>();
for (UserInfo user : mUserManager.getAliveUsers()) {
result.addAll(getAllAssociations(user.id));
}
@@ -1140,7 +921,7 @@
}
}
- private Set<Association> getAllAssociations(
+ private Set<AssociationInfo> getAllAssociations(
int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
return filter(
getAllAssociations(userId),
@@ -1156,7 +937,7 @@
mCurrentlyConnectedDevices.add(address);
for (UserInfo user : getAllUsers()) {
- for (Association association : getAllAssociations(user.id)) {
+ for (AssociationInfo association : getAllAssociations(user.id)) {
if (Objects.equals(address, association.getDeviceMacAddress())) {
if (association.getDeviceProfile() != null) {
Slog.i(LOG_TAG, "Granting role " + association.getDeviceProfile()
@@ -1171,7 +952,7 @@
onDeviceNearby(address);
}
- private void grantDeviceProfile(Association association) {
+ private void grantDeviceProfile(AssociationInfo association) {
Slog.i(LOG_TAG, "grantDeviceProfile(association = " + association + ")");
if (association.getDeviceProfile() != null) {
@@ -1278,7 +1059,7 @@
Date lastNearby = mDevicesLastNearby.valueAt(i);
if (isDeviceDisappeared(lastNearby)) {
- for (Association association : getAllAssociations(address)) {
+ for (AssociationInfo association : getAllAssociations(address)) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.unbindDevicePresenceListener(
association.getPackageName(), association.getUserId());
@@ -1320,12 +1101,12 @@
}
}
- private Set<Association> getAllAssociations(String deviceAddress) {
+ private Set<AssociationInfo> getAllAssociations(String deviceAddress) {
List<UserInfo> aliveUsers = mUserManager.getAliveUsers();
- Set<Association> result = new ArraySet<>();
+ Set<AssociationInfo> result = new ArraySet<>();
for (int i = 0, size = aliveUsers.size(); i < size; i++) {
UserInfo user = aliveUsers.get(i);
- for (Association association : getAllAssociations(user.id)) {
+ for (AssociationInfo association : getAllAssociations(user.id)) {
if (Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
result.add(association);
}
@@ -1349,7 +1130,7 @@
|| timestamp.getTime() - oldTimestamp.getTime() >= DEVICE_DISAPPEARED_TIMEOUT_MS;
if (justAppeared) {
Slog.i(LOG_TAG, "onDeviceNearby(justAppeared, address = " + address + ")");
- for (Association association : getAllAssociations(address)) {
+ for (AssociationInfo association : getAllAssociations(address)) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.onDeviceNotifyAppeared(association,
getContext(), mMainHandler);
@@ -1362,7 +1143,7 @@
Slog.i(LOG_TAG, "onDeviceDisappeared(address = " + address + ")");
boolean hasDeviceListeners = false;
- for (Association association : getAllAssociations(address)) {
+ for (AssociationInfo association : getAllAssociations(address)) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.onDeviceNotifyDisappeared(
association, getContext(), mMainHandler);
@@ -1431,7 +1212,7 @@
private List<ScanFilter> getBleScanFilters() {
ArrayList<ScanFilter> result = new ArrayList<>();
ArraySet<String> addressesSeen = new ArraySet<>();
- for (Association association : getAllAssociations()) {
+ for (AssociationInfo association : getAllAssociations()) {
String address = association.getDeviceMacAddress();
if (addressesSeen.contains(address)) {
continue;
@@ -1444,25 +1225,6 @@
return result;
}
- @NonNull
- private AndroidFuture<String> getDeviceProfilePermissionDescription(
- @Nullable String deviceProfile) {
- if (deviceProfile == null) {
- return AndroidFuture.completedFuture(null);
- }
-
- final AndroidFuture<String> result = new AndroidFuture<>();
- mPermissionControllerManager.getPrivilegesDescriptionStringForProfile(
- deviceProfile, FgThread.getExecutor(), desc -> {
- try {
- result.complete(String.valueOf(desc));
- } catch (Exception e) {
- result.completeExceptionally(e);
- }
- });
- return result;
- }
-
static int getFirstAssociationIdForUser(@UserIdInt int userId) {
// We want the IDs to start from 1, not 0.
return userId * ASSOCIATIONS_IDS_PER_USER_RANGE + 1;
diff --git a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
index 328a8b3..a79db2c 100644
--- a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
+++ b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
@@ -22,7 +22,7 @@
import static com.android.internal.util.CollectionUtils.filter;
import android.annotation.NonNull;
-import android.companion.Association;
+import android.companion.AssociationInfo;
import android.companion.CompanionDeviceService;
import android.companion.ICompanionDeviceService;
import android.content.ComponentName;
@@ -60,7 +60,7 @@
};
}
- void onDeviceNotifyAppeared(Association association, Context context, Handler handler) {
+ void onDeviceNotifyAppeared(AssociationInfo association, Context context, Handler handler) {
ServiceConnector<ICompanionDeviceService> primaryConnector =
getPrimaryServiceConnector(association, context, handler);
if (primaryConnector != null) {
@@ -71,7 +71,7 @@
}
}
- void onDeviceNotifyDisappeared(Association association, Context context, Handler handler) {
+ void onDeviceNotifyDisappeared(AssociationInfo association, Context context, Handler handler) {
ServiceConnector<ICompanionDeviceService> primaryConnector =
getPrimaryServiceConnector(association, context, handler);
if (primaryConnector != null) {
@@ -94,7 +94,7 @@
}
private ServiceConnector<ICompanionDeviceService> getPrimaryServiceConnector(
- Association association, Context context, Handler handler) {
+ AssociationInfo association, Context context, Handler handler) {
for (BoundService boundService: getDeviceListenerServiceConnector(association, context,
handler)) {
if (boundService.mIsPrimary) {
@@ -104,15 +104,15 @@
return null;
}
- private List<BoundService> getDeviceListenerServiceConnector(Association a, Context context,
+ private List<BoundService> getDeviceListenerServiceConnector(AssociationInfo a, Context context,
Handler handler) {
return mBoundServices.forUser(a.getUserId()).computeIfAbsent(
a.getPackageName(),
pkg -> createDeviceListenerServiceConnector(a, context, handler));
}
- private List<BoundService> createDeviceListenerServiceConnector(Association a, Context context,
- Handler handler) {
+ private List<BoundService> createDeviceListenerServiceConnector(AssociationInfo a,
+ Context context, Handler handler) {
List<ResolveInfo> resolveInfos = context
.getPackageManager()
.queryIntentServicesAsUser(new Intent(CompanionDeviceService.SERVICE_INTERFACE),
@@ -161,7 +161,7 @@
}
private boolean validatePackageInfo(List<ResolveInfo> packageResolveInfos,
- Association association) {
+ AssociationInfo association) {
if (packageResolveInfos.size() == 0 || packageResolveInfos.size() > 5) {
Slog.e(LOG_TAG, "Device presence listener package must have at least one and not "
+ "more than five CompanionDeviceService(s) declared. But "
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 73d45ad..5b8d7e5 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -34,7 +34,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.companion.Association;
+import android.companion.AssociationInfo;
import android.companion.DeviceId;
import android.os.Environment;
import android.util.AtomicFile;
@@ -179,11 +179,11 @@
* Reads previously persisted data for the given user "into" the provided containers.
*
* @param userId Android UserID
- * @param associationsOut a container to read the {@link Association}s "into".
+ * @param associationsOut a container to read the {@link AssociationInfo}s "into".
* @param previouslyUsedIdsPerPackageOut a container to read the used IDs "into".
*/
void readStateForUser(@UserIdInt int userId,
- @NonNull Set<Association> associationsOut,
+ @NonNull Set<AssociationInfo> associationsOut,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
Slog.i(LOG_TAG, "Reading associations for user " + userId + " from disk");
final AtomicFile file = getStorageFileForUser(userId);
@@ -244,7 +244,7 @@
* @param associations a set of user's associations.
* @param previouslyUsedIdsPerPackage a set previously used Association IDs for the user.
*/
- void persistStateForUser(@UserIdInt int userId, @NonNull Set<Association> associations,
+ void persistStateForUser(@UserIdInt int userId, @NonNull Set<AssociationInfo> associations,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
Slog.i(LOG_TAG, "Writing associations for user " + userId + " to disk");
if (DEBUG) Slog.d(LOG_TAG, " > " + associations);
@@ -257,7 +257,7 @@
}
private int readStateFromFileLocked(@UserIdInt int userId, @NonNull AtomicFile file,
- @NonNull String rootTag, @Nullable Set<Association> associationsOut,
+ @NonNull String rootTag, @Nullable Set<AssociationInfo> associationsOut,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
try (FileInputStream in = file.openRead()) {
final TypedXmlPullParser parser = Xml.resolvePullParser(in);
@@ -289,7 +289,7 @@
}
private void persistStateToFileLocked(@NonNull AtomicFile file,
- @Nullable Set<Association> associations,
+ @Nullable Set<AssociationInfo> associations,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
file.write(out -> {
try {
@@ -328,7 +328,7 @@
}
private static void readAssociationsV0(@NonNull TypedXmlPullParser parser,
- @UserIdInt int userId, @NonNull Set<Association> out)
+ @UserIdInt int userId, @NonNull Set<AssociationInfo> out)
throws XmlPullParserException, IOException {
requireStartOfTag(parser, XML_TAG_ASSOCIATIONS);
@@ -349,7 +349,7 @@
}
private static void readAssociationV0(@NonNull TypedXmlPullParser parser, @UserIdInt int userId,
- int associationId, @NonNull Set<Association> out) throws XmlPullParserException {
+ int associationId, @NonNull Set<AssociationInfo> out) throws XmlPullParserException {
requireStartOfTag(parser, XML_TAG_ASSOCIATION);
final String appPackage = readStringAttribute(parser, XML_ATTR_PACKAGE);
@@ -366,12 +366,12 @@
// "Convert" MAC address into a DeviceId.
final List<DeviceId> deviceIds = Arrays.asList(
new DeviceId(TYPE_MAC_ADDRESS, deviceAddress));
- out.add(new Association(associationId, userId, appPackage, deviceIds, profile,
+ out.add(new AssociationInfo(associationId, userId, appPackage, deviceIds, profile,
/* managedByCompanionApp */false, notify, timeApproved));
}
private static void readAssociationsV1(@NonNull TypedXmlPullParser parser,
- @UserIdInt int userId, @NonNull Set<Association> out)
+ @UserIdInt int userId, @NonNull Set<AssociationInfo> out)
throws XmlPullParserException, IOException {
requireStartOfTag(parser, XML_TAG_ASSOCIATIONS);
@@ -385,7 +385,7 @@
}
private static void readAssociationV1(@NonNull TypedXmlPullParser parser, @UserIdInt int userId,
- @NonNull Set<Association> out) throws XmlPullParserException, IOException {
+ @NonNull Set<AssociationInfo> out) throws XmlPullParserException, IOException {
requireStartOfTag(parser, XML_TAG_ASSOCIATION);
final int associationId = readIntAttribute(parser, XML_ATTR_ID);
@@ -406,8 +406,8 @@
deviceIds.add(new DeviceId(type, value));
}
- out.add(new Association(associationId, userId, appPackage, deviceIds, profile, managedByApp,
- notify, timeApproved));
+ out.add(new AssociationInfo(associationId, userId, appPackage, deviceIds, profile,
+ managedByApp, notify, timeApproved));
}
private static void readPreviouslyUsedIdsV1(@NonNull TypedXmlPullParser parser,
@@ -437,13 +437,13 @@
}
private static void writeAssociations(@NonNull XmlSerializer parent,
- @Nullable Set<Association> associations) throws IOException {
+ @Nullable Set<AssociationInfo> associations) throws IOException {
final XmlSerializer serializer = parent.startTag(null, XML_TAG_ASSOCIATIONS);
forEach(associations, it -> writeAssociation(serializer, it));
serializer.endTag(null, XML_TAG_ASSOCIATIONS);
}
- private static void writeAssociation(@NonNull XmlSerializer parent, @NonNull Association a)
+ private static void writeAssociation(@NonNull XmlSerializer parent, @NonNull AssociationInfo a)
throws IOException {
final XmlSerializer serializer = parent.startTag(null, XML_TAG_ASSOCIATION);
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
new file mode 100644
index 0000000..18cf6f8
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -0,0 +1,111 @@
+/*
+ * 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.IVirtualDeviceManager;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.util.ExceptionUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+
+/** @hide */
+@SuppressLint("LongLogTag")
+public class VirtualDeviceManagerService extends SystemService {
+
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = "VirtualDeviceManagerService";
+ private final VirtualDeviceManagerImpl mImpl;
+ @GuardedBy("mVirtualDevices")
+ private final ArrayList<VirtualDeviceImpl> mVirtualDevices = new ArrayList<>();
+
+ public VirtualDeviceManagerService(Context context) {
+ super(context);
+ mImpl = new VirtualDeviceManagerImpl();
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl);
+ }
+
+ private class VirtualDeviceImpl extends IVirtualDevice.Stub {
+
+ private VirtualDeviceImpl() {}
+
+ @Override
+ public void close() {
+ synchronized (mVirtualDevices) {
+ mVirtualDevices.remove(this);
+ }
+ }
+ }
+
+ class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
+
+ @Override
+ public IVirtualDevice createVirtualDevice() {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+ "createVirtualDevice");
+ VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl();
+ synchronized (mVirtualDevices) {
+ mVirtualDevices.add(virtualDevice);
+ }
+ return virtualDevice;
+ }
+
+ @Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (Throwable e) {
+ Slog.e(LOG_TAG, "Error during IPC", e);
+ throw ExceptionUtils.propagate(e, RemoteException.class);
+ }
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd,
+ @NonNull PrintWriter fout,
+ @Nullable String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), LOG_TAG, fout)) {
+ return;
+ }
+ fout.println("Created virtual devices: ");
+ synchronized (mVirtualDevices) {
+ for (VirtualDeviceImpl virtualDevice : mVirtualDevices) {
+ fout.println(virtualDevice.toString());
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 216224c..f135fbf 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -88,12 +88,21 @@
out: ["com/android/internal/art/ArtStatsLog.java"],
}
+genrule {
+ name: "statslog-contexthub-java-gen",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --java $(out) --module contexthub" +
+ " --javaPackage com.android.server.location.contexthub --javaClass ContextHubStatsLog",
+ out: ["com/android/server/location/contexthub/ContextHubStatsLog.java"],
+}
+
java_library_static {
name: "services.core.unboosted",
defaults: ["platform_service_defaults"],
srcs: [
":android.hardware.biometrics.face-V1-java-source",
":statslog-art-java-gen",
+ ":statslog-contexthub-java-gen",
":services.core-sources",
":services.core.protologsrc",
":dumpstate_aidl",
diff --git a/services/core/java/android/content/pm/OWNERS b/services/core/java/android/content/pm/OWNERS
index 5eed0b5..3993140 100644
--- a/services/core/java/android/content/pm/OWNERS
+++ b/services/core/java/android/content/pm/OWNERS
@@ -1 +1 @@
-include /core/java/android/content/pm/OWNERS
\ No newline at end of file
+include /PACKAGE_MANAGER_OWNERS
\ No newline at end of file
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 1e608f5..0146aa8 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -81,8 +81,6 @@
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
@@ -1435,11 +1433,7 @@
*/
public static final class HealthServiceWrapper {
private static final String TAG = "HealthServiceWrapper";
- public static final String INSTANCE_HEALTHD = "backup";
public static final String INSTANCE_VENDOR = "default";
- // All interesting instances, sorted by priority high -> low.
- private static final List<String> sAllInstances =
- Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
private final IServiceNotification mNotification = new Notification();
private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder");
@@ -1471,8 +1465,8 @@
}
/**
- * Start monitoring registration of new IHealth services. Only instances that are in
- * {@code sAllInstances} and in device / framework manifest are used. This function should
+ * Start monitoring registration of new IHealth services. Only instance
+ * {@link #INSTANCE_VENDOR} and in device / framework manifest are used. This function should
* only be called once.
*
* mCallback.onRegistration() is called synchronously (aka in init thread) before
@@ -1481,7 +1475,7 @@
* @throws RemoteException transaction error when talking to IServiceManager
* @throws NoSuchElementException if one of the following cases:
* - No service manager;
- * - none of {@code sAllInstances} are in manifests (i.e. not
+ * - {@link #INSTANCE_VENDOR} is not in manifests (i.e. not
* available on this device), or none of these instances are available to current
* process.
* @throws NullPointerException when supplier is null
@@ -1499,26 +1493,23 @@
// Initialize mLastService and call callback for the first time (in init thread)
IHealth newService = null;
- for (String name : sAllInstances) {
- traceBegin("HealthInitGetService_" + name);
- try {
- newService = healthSupplier.get(name);
- } catch (NoSuchElementException ex) {
- /* ignored, handled below */
- } finally {
- traceEnd();
- }
- if (newService != null) {
- mInstanceName = name;
- mLastService.set(newService);
- break;
- }
+ traceBegin("HealthInitGetService_" + INSTANCE_VENDOR);
+ try {
+ newService = healthSupplier.get(INSTANCE_VENDOR);
+ } catch (NoSuchElementException ex) {
+ /* ignored, handled below */
+ } finally {
+ traceEnd();
+ }
+ if (newService != null) {
+ mInstanceName = INSTANCE_VENDOR;
+ mLastService.set(newService);
}
if (mInstanceName == null || newService == null) {
throw new NoSuchElementException(String.format(
- "No IHealth service instance among %s is available. Perhaps no permission?",
- sAllInstances.toString()));
+ "IHealth service instance %s isn't available. Perhaps no permission?",
+ INSTANCE_VENDOR));
}
if (callback != null) {
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index 39321bf..9923274 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -301,17 +301,20 @@
protected void dumpSupportedUsers(@NonNull PrintWriter pw, @NonNull String prefix) {
final List<UserInfo> allUsers = UserManager.get(mContext).getUsers();
final List<Integer> supportedUsers = new ArrayList<>(allUsers.size());
- for (UserInfo user : allUsers) {
- supportedUsers.add(user.id);
+ for (int i = 0; i < allUsers.size(); i++) {
+ final UserInfo user = allUsers.get(i);
+ if (isUserSupported(new TargetUser(user))) {
+ supportedUsers.add(user.id);
+ }
}
- if (allUsers.isEmpty()) {
+ if (supportedUsers.isEmpty()) {
pw.print(prefix); pw.println("No supported users");
- } else {
- final int size = supportedUsers.size();
- pw.print(prefix); pw.print(size); pw.print(" supported user");
- if (size > 1) pw.print("s");
- pw.print(": "); pw.println(supportedUsers);
+ return;
}
+ final int size = supportedUsers.size();
+ pw.print(prefix); pw.print(size); pw.print(" supported user");
+ if (size > 1) pw.print("s");
+ pw.print(": "); pw.println(supportedUsers);
}
/**
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 1997897..b5623be 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -5110,6 +5110,29 @@
return didSomething;
}
+ void makeServicesNonForegroundLocked(final String pkg, final @UserIdInt int userId) {
+ final ServiceMap smap = mServiceMap.get(userId);
+ if (smap != null) {
+ ArrayList<ServiceRecord> fgsList = new ArrayList<>();
+ for (int i = 0; i < smap.mServicesByInstanceName.size(); i++) {
+ final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i);
+ if (sr.appInfo.packageName.equals(pkg) && sr.isForeground) {
+ fgsList.add(sr);
+ }
+ }
+
+ final int numServices = fgsList.size();
+ if (DEBUG_FOREGROUND_SERVICE) {
+ Slog.i(TAG_SERVICE, "Forcing " + numServices + " services out of foreground in u"
+ + userId + "/" + pkg);
+ }
+ for (int i = 0; i < numServices; i++) {
+ final ServiceRecord sr = fgsList.get(i);
+ setServiceForegroundInnerLocked(sr, 0, null, Service.STOP_FOREGROUND_REMOVE, 0);
+ }
+ }
+ }
+
void forceStopPackageLocked(String packageName, int userId) {
ServiceMap smap = mServiceMap.get(userId);
if (smap != null && smap.mActiveForegroundApps.size() > 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f6c1106..53f7575 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2397,6 +2397,7 @@
mBatteryStatsService.publish();
mAppOpsService.publish();
+ mProcessStats.publish();
Slog.d("AppOps", "AppOpsService published");
LocalServices.addService(ActivityManagerInternal.class, mInternal);
LocalManagerRegistry.addManager(ActivityManagerLocal.class,
@@ -3751,6 +3752,29 @@
}
@Override
+ public void makeServicesNonForeground(final String packageName, int userId) {
+ if (checkCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: makeServicesNonForeground() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ final int callingPid = Binder.getCallingPid();
+ userId = mUserController.handleIncomingUser(callingPid, Binder.getCallingUid(),
+ userId, true, ALLOW_FULL_ONLY, "makeServicesNonForeground", null);
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ makeServicesNonForegroundUnchecked(packageName, userId);
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
+ @Override
public void forceStopPackage(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
@@ -4058,6 +4082,13 @@
}
}
+ private void makeServicesNonForegroundUnchecked(final String packageName,
+ final @UserIdInt int userId) {
+ synchronized (this) {
+ mServices.makeServicesNonForegroundLocked(packageName, userId);
+ }
+ }
+
@GuardedBy("this")
private void forceStopPackageLocked(final String packageName, int uid, String reason) {
forceStopPackageLocked(packageName, UserHandle.getAppId(uid), false,
@@ -16399,6 +16430,11 @@
}
@Override
+ public void makeServicesNonForeground(String pkg, int userId) {
+ ActivityManagerService.this.makeServicesNonForegroundUnchecked(pkg, userId);
+ }
+
+ @Override
public void stopForegroundServicesForChannel(String pkg, int userId,
String channelId) {
synchronized (ActivityManagerService.this) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 685d606..0f71639 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -229,6 +229,8 @@
return runBugReport(pw);
case "force-stop":
return runForceStop(pw);
+ case "stop-fgs":
+ return runStopForegroundServices(pw);
case "fgs-notification-rate-limit":
return runFgsNotificationRateLimit(pw);
case "crash":
@@ -1105,6 +1107,22 @@
return 0;
}
+ int runStopForegroundServices(PrintWriter pw) throws RemoteException {
+ int userId = UserHandle.USER_SYSTEM;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+ mInterface.makeServicesNonForeground(getNextArgRequired(), userId);
+ return 0;
+ }
+
int runFgsNotificationRateLimit(PrintWriter pw) throws RemoteException {
final String toggleValue = getNextArgRequired();
final boolean enable;
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 34a0c1b..256cffd 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -38,8 +38,11 @@
import com.android.internal.app.procstats.IProcessStats;
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.app.procstats.ProcessStatsInternal;
import com.android.internal.app.procstats.ServiceState;
+import com.android.internal.app.procstats.UidState;
import com.android.internal.os.BackgroundThread;
+import com.android.server.LocalServices;
import java.io.File;
import java.io.FileDescriptor;
@@ -783,6 +786,64 @@
}
}
+ private SparseArray<long[]> getUidProcStateStatsOverTime(long minTime) {
+ final Parcel current = Parcel.obtain();
+ final ProcessStats stats = new ProcessStats();
+ long curTime;
+ synchronized (mLock) {
+ final long now = SystemClock.uptimeMillis();
+ mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
+ mProcessStats.mTimePeriodEndUptime = now;
+ stats.add(mProcessStats);
+ curTime = mProcessStats.mTimePeriodEndRealtime - mProcessStats.mTimePeriodStartRealtime;
+ }
+ if (curTime < minTime) {
+ try {
+ mFileLock.lock();
+ // Need to add in older stats to reach desired time.
+ ArrayList<String> files = getCommittedFilesLF(0, false, true);
+ if (files != null && files.size() > 0) {
+ int i = files.size() - 1;
+ while (i >= 0 && (stats.mTimePeriodEndRealtime
+ - stats.mTimePeriodStartRealtime) < minTime) {
+ AtomicFile file = new AtomicFile(new File(files.get(i)));
+ i--;
+ ProcessStats moreStats = new ProcessStats(false);
+ readLF(moreStats, file);
+ if (moreStats.mReadError == null) {
+ stats.add(moreStats);
+ } else {
+ Slog.w(TAG, "Failure reading " + files.get(i + 1) + "; "
+ + moreStats.mReadError);
+ continue;
+ }
+ }
+ }
+ } finally {
+ mFileLock.unlock();
+ }
+ }
+ final SparseArray<UidState> uidStates = stats.mUidStates;
+ final SparseArray<long[]> results = new SparseArray<>();
+ for (int i = 0, size = uidStates.size(); i < size; i++) {
+ final int uid = uidStates.keyAt(i);
+ final UidState uidState = uidStates.valueAt(i);
+ results.put(uid, uidState.getAggregatedDurationsInStates());
+ }
+ return results;
+ }
+
+ void publish() {
+ LocalServices.addService(ProcessStatsInternal.class, new LocalService());
+ }
+
+ private final class LocalService extends ProcessStatsInternal {
+ @Override
+ public SparseArray<long[]> getUidProcStateStatsOverTime(long minTime) {
+ return ProcessStatsService.this.getUidProcStateStatsOverTime(minTime);
+ }
+ }
+
private void dumpAggregatedStats(PrintWriter pw, long aggregateHours, long now,
String reqPackage, boolean isCompact, boolean dumpDetails, boolean dumpFullDetails,
boolean dumpAll, boolean activeOnly, int section) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 4cb786a..e5e29a6 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1774,19 +1774,36 @@
MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION);
MESSAGES_MUTE_MUSIC.add(MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT);
MESSAGES_MUTE_MUSIC.add(MSG_IIL_SET_FORCE_BT_A2DP_USE);
- MESSAGES_MUTE_MUSIC.add(MSG_REPORT_NEW_ROUTES_A2DP);
}
private AtomicBoolean mMusicMuted = new AtomicBoolean(false);
+ boolean messageMutesMusic(int message) {
+ if (message == 0) {
+ return false;
+ }
+ // Do not mute if we are disconnecting an A2DP device and music is playing
+ // on a wired headset.
+ if ((message == MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED
+ || message == MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION)
+ && AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
+ && mDeviceInventory.DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(
+ mAudioService.getDevicesForStream(AudioSystem.STREAM_MUSIC))) {
+ return false;
+ }
+ return true;
+ }
+
/** Mutes or unmutes music according to pending A2DP messages */
private void checkMessagesMuteMusic(int message) {
- boolean mute = message != 0;
+ boolean mute = messageMutesMusic(message);
if (!mute) {
for (int msg : MESSAGES_MUTE_MUSIC) {
if (mBrokerHandler.hasMessages(msg)) {
- mute = true;
- break;
+ if (messageMutesMusic(msg)) {
+ mute = true;
+ break;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 6c3c736..5909f80 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -581,7 +581,7 @@
mDeviceBroker.postObserveDevicesForAllStreams();
}
- private static final Set<Integer> DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET;
+ /* package */ static final Set<Integer> DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET;
static {
DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET = new HashSet<>();
DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 34277a2..2cd63b5 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6214,9 +6214,15 @@
*/
public @AudioManager.DeviceVolumeBehavior
int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
+ Objects.requireNonNull(device);
// verify permissions
enforceQueryStateOrModifyRoutingPermission();
+ return getDeviceVolumeBehaviorInt(device);
+ }
+
+ private @AudioManager.DeviceVolumeBehavior
+ int getDeviceVolumeBehaviorInt(@NonNull AudioDeviceAttributes device) {
// translate Java device type to native device type (for the devices masks for full / fixed)
final int audioSystemDeviceOut = AudioDeviceInfo.convertDeviceTypeToInternalDevice(
device.getType());
@@ -6244,6 +6250,29 @@
return AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE;
}
+ /**
+ * @see AudioManager#isVolumeFixed()
+ * Note there are no permission checks on this operation, as this is part of API 21
+ * @return true if the current device's volume behavior for media is
+ * DEVICE_VOLUME_BEHAVIOR_FIXED
+ */
+ public boolean isVolumeFixed() {
+ if (mUseFixedVolume) {
+ return true;
+ }
+ final AudioAttributes attributes = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .build();
+ // calling getDevice*Int to bypass permission check
+ final List<AudioDeviceAttributes> devices = getDevicesForAttributesInt(attributes);
+ for (AudioDeviceAttributes device : devices) {
+ if (getDeviceVolumeBehaviorInt(device) == AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/*package*/ static final int CONNECTION_STATE_DISCONNECTED = 0;
/*package*/ static final int CONNECTION_STATE_CONNECTED = 1;
/**
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 031f6ee..61b8ded 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -168,6 +168,10 @@
return Utils.isKeyguard(getContext(), getOwnerString());
}
+ private boolean isSettings() {
+ return Utils.isSettings(getContext(), getOwnerString());
+ }
+
@Override
protected boolean isCryptoOperation() {
return mOperationId != 0;
@@ -499,6 +503,8 @@
protected int getShowOverlayReason() {
if (isKeyguard()) {
return BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
+ } else if (isSettings()) {
+ return BiometricOverlayConstants.REASON_AUTH_SETTINGS;
} else if (isBiometricPrompt()) {
return BiometricOverlayConstants.REASON_AUTH_BP;
} else {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index ca051e9..e4d5fba 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -229,6 +229,7 @@
@Override
public void onLockoutTimed(long durationMillis) {
+ super.onLockoutTimed(durationMillis);
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
@@ -246,6 +247,7 @@
@Override
public void onLockoutPermanent() {
+ super.onLockoutPermanent();
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 091e6c4..a56a8ea 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -227,7 +227,7 @@
subscriberId = tele.getSubscriberId();
mNetworkTemplate = new NetworkTemplate(
NetworkTemplate.MATCH_MOBILE, subscriberId, new String[] { subscriberId },
- null, NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+ null, NetworkStats.METERED_YES, NetworkStats.ROAMING_ALL,
NetworkStats.DEFAULT_NETWORK_NO, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
SUBSCRIBER_ID_MATCH_RULE_EXACT);
mUsageCallback = new UsageCallback() {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 5178f35..8481ad0 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2117,9 +2117,6 @@
pw.println();
mLogicalDisplayMapper.dumpLocked(pw);
- pw.println();
- mDisplayModeDirector.dump(pw);
-
final int callbackCount = mCallbacks.size();
pw.println();
pw.println("Callbacks: size=" + callbackCount);
@@ -2142,6 +2139,8 @@
pw.println();
mPersistentDataStore.dump(pw);
}
+ pw.println();
+ mDisplayModeDirector.dump(pw);
}
private static float[] getFloatArray(TypedArray array) {
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 87b9e6a..c82b16d 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -524,7 +524,10 @@
*/
public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
synchronized (mLock) {
- mAlwaysRespectAppRequest = enabled;
+ if (mAlwaysRespectAppRequest != enabled) {
+ mAlwaysRespectAppRequest = enabled;
+ notifyDesiredDisplayModeSpecsChangedLocked();
+ }
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 147050c..2f22d33 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -48,7 +48,7 @@
final class DisplayPowerState {
private static final String TAG = "DisplayPowerState";
- private static boolean DEBUG = false;
+ private static final boolean DEBUG = false;
private static String COUNTER_COLOR_FADE = "ColorFadeLevel";
private final Handler mHandler;
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 5bc69943..76754d3 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -117,7 +117,8 @@
}
public void startDream(Binder token, ComponentName name,
- boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
+ boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock,
+ ComponentName overlayComponentName) {
stopDream(true /*immediate*/, "starting new dream");
Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream");
@@ -138,6 +139,7 @@
Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
intent.setComponent(name);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(DreamService.EXTRA_DREAM_OVERLAY_COMPONENT, overlayComponentName);
try {
if (!mContext.bindServiceAsUser(intent, mCurrentDream,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 3a7220f7..258689a 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -95,6 +95,8 @@
private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+ private ComponentName mDreamOverlayServiceName;
+
private AmbientDisplayConfiguration mDozeConfig;
@VisibleForTesting
@@ -421,7 +423,8 @@
if (!mCurrentDreamName.equals(mAmbientDisplayComponent)) {
mUiEventLogger.log(DreamManagerEvent.DREAM_START);
}
- mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock);
+ mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock,
+ mDreamOverlayServiceName);
}));
}
@@ -592,6 +595,15 @@
}
@Override // Binder call
+ public void registerDreamOverlayService(ComponentName overlayComponent) {
+ checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+
+ // Store the overlay service component so that it can be passed to the dream when it is
+ // invoked.
+ mDreamOverlayServiceName = overlayComponent;
+ }
+
+ @Override // Binder call
public ComponentName getDefaultDreamComponentForUser(int userId) {
checkPermission(android.Manifest.permission.READ_DREAM_STATE);
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index f356c36..3aa2f42 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -208,6 +208,12 @@
void init() {
assertRunOnServiceThread();
mPreferredAddress = getPreferredAddress();
+ if (mHandler.hasMessages(MSG_DISABLE_DEVICE_TIMEOUT)) {
+ // Remove and trigger the queued message for clearing all actions when going to standby.
+ // This is necessary because the device may wake up before the message is triggered.
+ mHandler.removeMessages(MSG_DISABLE_DEVICE_TIMEOUT);
+ handleDisableDeviceTimeout();
+ }
mPendingActionClearedCallback = null;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index b5bb8bd..69f7af2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -3166,7 +3166,7 @@
Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
devices.remove(device);
if (devices.isEmpty()) {
- onStandbyCompleted(standbyAction);
+ onPendingActionsCleared(standbyAction);
// We will not clear local devices here, since some OEM/SOC will keep passing
// the received packets until the application processor enters to the sleep
// actually.
@@ -3227,10 +3227,17 @@
mHdmiCecNetwork.clearLocalDevices();
}
+ /**
+ * Normally called after all devices have cleared their pending actions, to execute the final
+ * phase of the standby flow.
+ *
+ * This can also be called during wakeup, when pending actions are cleared after failing to be
+ * cleared during standby. In this case, it does not execute the standby flow.
+ */
@ServiceThreadOnly
- private void onStandbyCompleted(int standbyAction) {
+ private void onPendingActionsCleared(int standbyAction) {
assertRunOnServiceThread();
- Slog.v(TAG, "onStandbyCompleted");
+ Slog.v(TAG, "onPendingActionsCleared");
if (!mPowerStatusController.isPowerStatusTransientToStandby()) {
return;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 686926f..1c04d8c 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -49,6 +49,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -184,6 +185,7 @@
}
public ContextHubService(Context context) {
+ long startTimeNs = SystemClock.elapsedRealtimeNanos();
mContext = context;
mContextHubWrapper = getContextHubWrapper();
@@ -205,6 +207,9 @@
Log.e(TAG, "RemoteException while getting Context Hub info", e);
hubInfo = new Pair(Collections.emptyList(), Collections.emptyList());
}
+ long bootTimeNs = SystemClock.elapsedRealtimeNanos() - startTimeNs;
+ int numContextHubs = hubInfo.first.size();
+ ContextHubStatsLog.write(ContextHubStatsLog.CONTEXT_HUB_BOOTED, bootTimeNs, numContextHubs);
mContextHubIdToInfoMap = Collections.unmodifiableMap(
ContextHubServiceUtil.createContextHubInfoMap(hubInfo.first));
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 83de0b3..cbca48c9 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1996,6 +1996,11 @@
+ TimeUtils.formatDuration(delayMs));
}
+ if (mDelayedRegister != null) {
+ mAlarmHelper.cancel(mDelayedRegister);
+ mDelayedRegister = null;
+ }
+
mDelayedRegister = new OnAlarmListener() {
@Override
public void onAlarm() {
diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
index d285c43..4873467 100644
--- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
+++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
@@ -40,7 +40,7 @@
* Monitors the state changes of audio players.
*/
class AudioPlayerStateMonitor {
- private static boolean DEBUG = MediaSessionService.DEBUG;
+ private static final boolean DEBUG = MediaSessionService.DEBUG;
private static String TAG = "AudioPlayerStateMonitor";
private static AudioPlayerStateMonitor sInstance;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index b45d87f..d0aa40b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -15,12 +15,14 @@
*/
package com.android.server.net;
+import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
import static android.net.INetd.FIREWALL_RULE_ALLOW;
import static android.net.INetd.FIREWALL_RULE_DENY;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED;
@@ -29,6 +31,7 @@
import static android.os.PowerExemptionManager.reasonCodeToString;
import static android.os.Process.INVALID_UID;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.ProcessCapability;
import android.net.NetworkPolicyManager;
@@ -81,13 +84,18 @@
private final Object mLock = new Object();
- void networkBlocked(int uid, UidBlockedState uidBlockedState) {
+ void networkBlocked(int uid, @Nullable UidBlockedState uidBlockedState) {
synchronized (mLock) {
if (LOGD || uid == mDebugUid) {
Slog.d(TAG, "Blocked state of uid: " + uidBlockedState.toString());
}
- mNetworkBlockedBuffer.networkBlocked(uid, uidBlockedState.blockedReasons,
- uidBlockedState.allowedReasons, uidBlockedState.effectiveBlockedReasons);
+ if (uidBlockedState == null) {
+ mNetworkBlockedBuffer.networkBlocked(uid, BLOCKED_REASON_NONE, ALLOWED_REASON_NONE,
+ BLOCKED_REASON_NONE);
+ } else {
+ mNetworkBlockedBuffer.networkBlocked(uid, uidBlockedState.blockedReasons,
+ uidBlockedState.allowedReasons, uidBlockedState.effectiveBlockedReasons);
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f14f7695..e5df3e7 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2824,6 +2824,12 @@
mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
mPreferencesHelper.updateNotificationChannel(pkg, uid, channel, true);
+ if (mEnableAppSettingMigration) {
+ if (mPreferencesHelper.onlyHasDefaultChannel(pkg, uid)) {
+ mPermissionHelper.setNotificationPermission(pkg, UserHandle.getUserId(uid),
+ channel.getImportance() != IMPORTANCE_NONE, true);
+ }
+ }
maybeNotifyChannelOwner(pkg, uid, preUpdate, channel);
if (!fromListener) {
@@ -5077,6 +5083,27 @@
@Override
public boolean matchesCallFilter(Bundle extras) {
+ // Because matchesCallFilter may use contact data to filter calls, the callers of this
+ // method need to either have notification listener access or permission to read
+ // contacts.
+ boolean listenerAccess = false;
+ try {
+ String[] pkgNames = mPackageManager.getPackagesForUid(Binder.getCallingUid());
+ for (int i = 0; i < pkgNames.length; i++) {
+ // in most cases there should only be one package here
+ listenerAccess |= mListeners.hasAllowedListener(pkgNames[i],
+ Binder.getCallingUserHandle().getIdentifier());
+ }
+ } catch (RemoteException e) {
+ } finally {
+ if (!listenerAccess) {
+ getContext().enforceCallingPermission(
+ Manifest.permission.READ_CONTACTS,
+ "matchesCallFilter requires listener permissions or "
+ + "contacts read access");
+ }
+ }
+
return mZenModeHelper.matchesCallFilter(
Binder.getCallingUserHandle(),
extras,
@@ -10948,6 +10975,23 @@
}
return false;
}
+
+ // Returns whether there is a component with listener access granted that is associated
+ // with the given package name / user ID.
+ boolean hasAllowedListener(String packageName, int userId) {
+ if (packageName == null) {
+ return false;
+ }
+
+ // Loop through allowed components to compare package names
+ List<ComponentName> allowedComponents = getAllowedComponents(userId);
+ for (int i = 0; i < allowedComponents.size(); i++) {
+ if (allowedComponents.get(i).getPackageName().equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
// TODO (b/194833441): remove when we've fully migrated to a permission
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index b94721a..c74813a 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1043,9 +1043,11 @@
r.channels.put(updatedChannel.getId(), updatedChannel);
if (onlyHasDefaultChannel(pkg, uid)) {
- // copy settings to app level so they are inherited by new channels
- // when the app migrates
- r.importance = updatedChannel.getImportance();
+ if (!mPermissionHelper.isMigrationEnabled()) {
+ // copy settings to app level so they are inherited by new channels
+ // when the app migrates
+ r.importance = updatedChannel.getImportance();
+ }
r.priority = updatedChannel.canBypassDnd()
? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
r.visibility = updatedChannel.getLockscreenVisibility();
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index aa4d6bb..569d651 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -798,7 +798,8 @@
for (int n = 0; n < count; n++) {
ResolveInfo info = candidates.get(n);
if (blockInstant && (info.isInstantAppAvailable
- || isInstantApp(info.activityInfo.packageName, userId))) {
+ || isInstantAppInternal(info.activityInfo.packageName, userId,
+ Process.SYSTEM_UID))) {
continue;
}
@@ -1075,7 +1076,8 @@
resolveInfos.remove(i);
continue;
}
- if (blockInstant && isInstantApp(info.activityInfo.packageName, userId)) {
+ if (blockInstant && isInstantAppInternal(
+ info.activityInfo.packageName, userId, Process.SYSTEM_UID)) {
resolveInfos.remove(i);
continue;
}
diff --git a/services/core/java/com/android/server/pm/IncrementalProgressListener.java b/services/core/java/com/android/server/pm/IncrementalProgressListener.java
index bb797cb..43a938e 100644
--- a/services/core/java/com/android/server/pm/IncrementalProgressListener.java
+++ b/services/core/java/com/android/server/pm/IncrementalProgressListener.java
@@ -37,7 +37,20 @@
if (ps == null) {
return;
}
- ps.setLoadingProgress(progress);
+
+ boolean wasLoading = ps.isPackageLoading();
+ // Due to asynchronous progress reporting, incomplete progress might be received
+ // after the app is migrated off incremental. Ignore such progress updates.
+ if (wasLoading) {
+ ps.setLoadingProgress(progress);
+ // Only report the state change when loading state changes from loading to not
+ if (!ps.isPackageLoading()) {
+ // Unregister progress listener
+ mPm.mIncrementalManager.unregisterLoadingProgressCallbacks(ps.getPathString());
+ // Make sure the information is preserved
+ mPm.scheduleWriteSettingsLocked();
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
deleted file mode 100644
index 3101ca7..0000000
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-import android.content.pm.IncrementalStatesInfo;
-import android.os.Handler;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.function.pooled.PooledLambda;
-
-/**
- * Manages state transitions of a package installed on Incremental File System. Currently manages:
- * 1. loading state (whether a package is still loading or has been fully loaded).
- *
- * The following events might change the states of a package:
- * 1. Installation commit
- * 2. Loading progress changes
- *
- * @hide
- */
-public final class IncrementalStates {
- private static final String TAG = "IncrementalStates";
- private static final boolean DEBUG = false;
- private final Handler mHandler = BackgroundThread.getHandler();
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private final LoadingState mLoadingState;
- @GuardedBy("mLock")
- private Callback mCallback = null;
-
- public IncrementalStates() {
- // By default the package is not fully loaded (i.e., is loading)
- this(true, 0);
- }
-
- public IncrementalStates(boolean isLoading, float loadingProgress) {
- mLoadingState = new LoadingState(isLoading, loadingProgress);
- }
-
- /**
- * Callback interface to report that the loading state of this package has changed.
- */
- public interface Callback {
- /**
- * Reports that package is fully loaded.
- */
- void onPackageFullyLoaded();
- }
-
- /**
- * By calling this method, the caller indicates that package installation has just been
- * committed. Set the initial loading state after the package
- * is committed. Incremental packages are by-default loading; non-Incremental packages are not.
- *
- * @param isIncremental whether a package is installed on Incremental or not.
- */
- public void onCommit(boolean isIncremental) {
- if (DEBUG) {
- Slog.i(TAG, "received package commit event");
- }
- if (!isIncremental) {
- synchronized (mLock) {
- updateProgressLocked(1.0f);
- }
- onLoadingStateChanged();
- }
- }
-
- private void onLoadingStateChanged() {
- mHandler.post(PooledLambda.obtainRunnable(
- IncrementalStates::reportFullyLoaded,
- IncrementalStates.this).recycleOnUse());
- }
-
- private void reportFullyLoaded() {
- final Callback callback;
- synchronized (mLock) {
- callback = mCallback;
- }
- if (callback != null) {
- callback.onPackageFullyLoaded();
- }
- }
-
- /**
- * Use the specified callback to report state changing events.
- *
- * @param callback Object to report new state.
- */
- public void setCallback(Callback callback) {
- if (DEBUG) {
- Slog.i(TAG, "registered callback");
- }
- synchronized (mLock) {
- mCallback = callback;
- }
- }
-
- /**
- * Update the package loading progress to specified value.
- *
- * @param progress Value between [0, 1].
- */
- public void setProgress(float progress) {
- final boolean oldLoadingState;
- final boolean newLoadingState;
- synchronized (mLock) {
- oldLoadingState = mLoadingState.isLoading();
- if (oldLoadingState) {
- // Due to asynchronous progress reporting, incomplete progress might be received
- // after the app is migrated off incremental. Ignore such progress updates.
- updateProgressLocked(progress);
- }
- newLoadingState = mLoadingState.isLoading();
- }
- if (oldLoadingState && !newLoadingState) {
- // Only report the state change when loading state changes from true to false
- onLoadingStateChanged();
- }
- }
-
- /**
- * @return all current states in a Parcelable.
- */
- public IncrementalStatesInfo getIncrementalStatesInfo() {
- synchronized (mLock) {
- return new IncrementalStatesInfo(
- mLoadingState.isLoading(),
- mLoadingState.getProgress());
- }
- }
-
- private void updateProgressLocked(float progress) {
- if (DEBUG) {
- Slog.i(TAG, "received progress update: " + progress);
- }
- mLoadingState.setProgress(progress);
- if (Math.abs(1.0f - progress) < 0.00000001f) {
- if (DEBUG) {
- Slog.i(TAG, "package is fully loaded");
- }
- mLoadingState.setProgress(1.0f);
- if (mLoadingState.isLoading()) {
- mLoadingState.adoptNewLoadingStateLocked(false);
- }
- }
- }
-
- private class LoadingState {
- private boolean mIsLoading;
- private float mProgress;
-
- LoadingState(boolean isLoading, float loadingProgress) {
- mIsLoading = isLoading;
- // loading progress is reset to 1 if loading has finished
- mProgress = isLoading ? loadingProgress : 1;
- }
-
- public boolean isLoading() {
- return mIsLoading;
- }
-
- public float getProgress() {
- return mProgress;
- }
-
- public void setProgress(float progress) {
- mProgress = progress;
- }
-
- public void adoptNewLoadingStateLocked(boolean nextState) {
- if (DEBUG) {
- Slog.i(TAG, "Loading state changed from " + mIsLoading + " to " + nextState);
- }
- mIsLoading = nextState;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof LoadingState)) {
- return false;
- }
- LoadingState l = (LoadingState) o;
- return l.mIsLoading == mIsLoading && l.mProgress == mProgress;
- }
-
- @Override
- public int hashCode() {
- int hashCode = Boolean.hashCode(mIsLoading);
- hashCode = 31 * hashCode + Float.hashCode(mProgress);
- return hashCode;
- }
- }
-
-
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof IncrementalStates)) {
- return false;
- }
- IncrementalStates l = (IncrementalStates) o;
- return l.mLoadingState.equals(mLoadingState);
- }
-
- @Override
- public int hashCode() {
- return mLoadingState.hashCode();
- }
-}
diff --git a/services/core/java/com/android/server/pm/IncrementalStatesCallback.java b/services/core/java/com/android/server/pm/IncrementalStatesCallback.java
deleted file mode 100644
index 478c99b..0000000
--- a/services/core/java/com/android/server/pm/IncrementalStatesCallback.java
+++ /dev/null
@@ -1,46 +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.server.pm;
-
-/**
- * Package states callback, used to listen for package state changes and send broadcasts
- */
-final class IncrementalStatesCallback implements IncrementalStates.Callback {
- private final String mPackageName;
- private final PackageManagerService mPm;
-
- IncrementalStatesCallback(String packageName, PackageManagerService pm) {
- mPackageName = packageName;
- mPm = pm;
- }
-
- @Override
- public void onPackageFullyLoaded() {
- final String codePath;
- synchronized (mPm.mLock) {
- final PackageSetting ps = mPm.mSettings.getPackageLPr(mPackageName);
- if (ps == null) {
- return;
- }
- codePath = ps.getPathString();
- }
- // Unregister progress listener
- mPm.mIncrementalManager.unregisterLoadingProgressCallbacks(codePath);
- // Make sure the information is preserved
- mPm.scheduleWriteSettingsLocked();
- }
-}
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index 722198f..5ff0a6f 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -172,9 +172,16 @@
scanSystemDirs(packageParser, executorService);
// Parse overlay configuration files to set default enable state, mutability, and
// priority of system overlays.
+ final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
+ for (ApexManager.ActiveApexInfo apexInfo : mPm.mApexManager.getActiveApexInfos()) {
+ for (String packageName : mPm.mApexManager.getApksInApex(apexInfo.apexModuleName)) {
+ apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
+ }
+ }
OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
consumer -> mPm.forEachPackage(
- pkg -> consumer.accept(pkg, pkg.isSystem())));
+ pkg -> consumer.accept(pkg, pkg.isSystem(),
+ apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
cleanupSystemPackagesAndInstallStubs(packageParser, executorService, packageSettings,
startTime, userIds);
packageParser.close();
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 7569900..4c70cda 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -59,6 +59,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.incremental.IncrementalManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -455,7 +456,10 @@
if (pkgSetting.getInstantApp(userId)) {
mPm.mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.getAppId());
}
- pkgSetting.setStatesOnCommit();
+
+ if (!IncrementalManager.isIncrementalPath(pkgSetting.getPathString())) {
+ pkgSetting.setLoadingProgress(1f);
+ }
return pkg;
}
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index bfb5f76..e138188 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -1781,9 +1781,6 @@
final String codePath = ps.getPathString();
if (IncrementalManager.isIncrementalPath(codePath)
&& mPm.mIncrementalManager != null) {
- final IncrementalStatesCallback incrementalStatesCallback =
- new IncrementalStatesCallback(ps.getPackageName(), mPm);
- ps.setIncrementalStatesCallback(incrementalStatesCallback);
mPm.mIncrementalManager.registerLoadingProgressCallback(codePath,
new IncrementalProgressListener(ps.getPackageName(), mPm));
}
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 3233819..1bdc9f3 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -3,27 +3,25 @@
jsharkey@android.com
jsharkey@google.com
narayan@google.com
-patb@google.com
svetoslavganov@android.com
svetoslavganov@google.com
-toddke@android.com
-toddke@google.com
+include /PACKAGE_MANAGER_OWNERS
# apex support
per-file ApexManager.java = dariofreni@google.com, ioffe@google.com, olilan@google.com
per-file StagingManager.java = dariofreni@google.com, ioffe@google.com, olilan@google.com
# dex
-per-file AbstractStatsBase.java = calin@google.com, ngeoffray@google.com
-per-file BackgroundDexOptService.java = calin@google.com, ngeoffray@google.com
-per-file CompilerStats.java = calin@google.com, ngeoffray@google.com
-per-file DynamicCodeLoggingService.java = alanstokes@google.com, calin@google.com, ngeoffray@google.com
-per-file InstructionSets.java = calin@google.com, ngeoffray@google.com
-per-file OtaDexoptService.java = calin@google.com, ngeoffray@google.com
-per-file OtaDexoptShellCommand.java = calin@google.com, ngeoffray@google.com
-per-file PackageDexOptimizer.java = calin@google.com, ngeoffray@google.com
-per-file PackageManagerServiceCompilerMapping.java = calin@google.com, ngeoffray@google.com
-per-file PackageUsage.java = calin@google.com, ngeoffray@google.com
+per-file AbstractStatsBase.java = file:dex/OWNERS
+per-file BackgroundDexOptService.java = file:dex/OWNERS
+per-file CompilerStats.java = file:dex/OWNERS
+per-file DynamicCodeLoggingService.java = file:dex/OWNERS
+per-file InstructionSets.java = file:dex/OWNERS
+per-file OtaDexoptService.java = file:dex/OWNERS
+per-file OtaDexoptShellCommand.java = file:dex/OWNERS
+per-file PackageDexOptimizer.java = file:dex/OWNERS
+per-file PackageManagerServiceCompilerMapping.java = file:dex/OWNERS
+per-file PackageUsage.java = file:dex/OWNERS
# multi user / cross profile
per-file CrossProfileAppsServiceImpl.java = omakoto@google.com, yamasani@google.com
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2595bb4..fc266c8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -174,6 +174,7 @@
private final File mSessionsDir;
private final InternalCallback mInternalCallback = new InternalCallback();
+ private final PackageSessionVerifier mSessionVerifier;
/**
* Used for generating session IDs. Since this is created at boot time,
@@ -257,8 +258,9 @@
mSessionsDir.mkdirs();
mApexManager = ApexManager.getInstance();
- mStagingManager = new StagingManager(context, apexParserSupplier,
- mInstallThread.getLooper());
+ mStagingManager = new StagingManager(context);
+ mSessionVerifier = new PackageSessionVerifier(context, mPm, mApexManager,
+ apexParserSupplier, mInstallThread.getLooper());
LocalServices.getService(SystemServiceManager.class).startService(
new Lifecycle(context, this));
@@ -1160,6 +1162,11 @@
}
@Override
+ public PackageSessionVerifier getSessionVerifier() {
+ return mSessionVerifier;
+ }
+
+ @Override
public void bypassNextStagedInstallerCheck(boolean value) {
if (!isCalledBySystemOrShell(Binder.getCallingUid())) {
throw new SecurityException("Caller not allowed to bypass staged installer check");
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 3805977..97f8dc2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -732,11 +732,10 @@
}
/**
- * Notified by the staging manager or PIS that pre-reboot verification has ended.
+ * Called when pre-reboot verification has ended.
* Now it is safe to clean up the session if {@link #abandon()} has been called previously.
*/
- @Override
- public void notifyEndPreRebootVerification() {
+ private void notifyEndPreRebootVerification() {
synchronized (mLock) {
if (!mInPreRebootVerification) {
throw new IllegalStateException("Pre-reboot verification not started");
@@ -2382,6 +2381,16 @@
private void verify() {
try {
+ List<PackageInstallerSession> children = getChildSessions();
+ if (isMultiPackage()) {
+ for (PackageInstallerSession child : children) {
+ child.prepareInheritedFiles();
+ child.parseApkAndExtractNativeLibraries();
+ }
+ } else {
+ prepareInheritedFiles();
+ parseApkAndExtractNativeLibraries();
+ }
verifyNonStaged();
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
@@ -2401,6 +2410,118 @@
}
}
+ /**
+ * Prepares staged directory with any inherited APKs.
+ */
+ private void prepareInheritedFiles() throws PackageManagerException {
+ if (isApexSession() || params.mode != SessionParams.MODE_INHERIT_EXISTING) {
+ return;
+ }
+ synchronized (mLock) {
+ if (mRelinquished) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session relinquished");
+ }
+ if (mDestroyed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session destroyed");
+ }
+ if (!mSealed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session not sealed");
+ }
+ // Inherit any packages and native libraries from existing install that
+ // haven't been overridden.
+ try {
+ final List<File> fromFiles = mResolvedInheritedFiles;
+ final File toDir = stageDir;
+
+ if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
+ if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
+ throw new IllegalStateException("mInheritedFilesBase == null");
+ }
+
+ if (isLinkPossible(fromFiles, toDir)) {
+ if (!mResolvedInstructionSets.isEmpty()) {
+ final File oatDir = new File(toDir, "oat");
+ createOatDirs(mResolvedInstructionSets, oatDir);
+ }
+ // pre-create lib dirs for linking if necessary
+ if (!mResolvedNativeLibPaths.isEmpty()) {
+ for (String libPath : mResolvedNativeLibPaths) {
+ // "/lib/arm64" -> ["lib", "arm64"]
+ final int splitIndex = libPath.lastIndexOf('/');
+ if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
+ Slog.e(TAG,
+ "Skipping native library creation for linking due"
+ + " to invalid path: " + libPath);
+ continue;
+ }
+ final String libDirPath = libPath.substring(1, splitIndex);
+ final File libDir = new File(toDir, libDirPath);
+ if (!libDir.exists()) {
+ NativeLibraryHelper.createNativeLibrarySubdir(libDir);
+ }
+ final String archDirPath = libPath.substring(splitIndex + 1);
+ NativeLibraryHelper.createNativeLibrarySubdir(
+ new File(libDir, archDirPath));
+ }
+ }
+ linkFiles(fromFiles, toDir, mInheritedFilesBase);
+ } else {
+ // TODO: this should delegate to DCS so the system process
+ // avoids holding open FDs into containers.
+ copyFiles(fromFiles, toDir);
+ }
+ } catch (IOException e) {
+ throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
+ "Failed to inherit existing install", e);
+ }
+ }
+ }
+
+ private void parseApkAndExtractNativeLibraries() throws PackageManagerException {
+ synchronized (mLock) {
+ if (mRelinquished) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session relinquished");
+ }
+ if (mDestroyed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session destroyed");
+ }
+ if (!mSealed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session not sealed");
+ }
+ Objects.requireNonNull(mPackageName);
+ Objects.requireNonNull(mSigningDetails);
+ Objects.requireNonNull(mResolvedBaseFile);
+ final PackageLite result;
+ if (!isApexSession()) {
+ // For mode inherit existing, it would link/copy existing files to stage dir in
+ // prepareInheritedFiles(). Therefore, we need to parse the complete package in
+ // stage dir here.
+ // Besides, PackageLite may be null for staged sessions that don't complete
+ // pre-reboot verification.
+ result = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
+ } else {
+ result = getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0);
+ }
+ if (result != null) {
+ mPackageLite = result;
+ if (!isApexSession()) {
+ synchronized (mProgressLock) {
+ mInternalProgress = 0.5f;
+ computeProgressLocked(true);
+ }
+ extractNativeLibraries(
+ mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
+ }
+ }
+ }
+ }
+
private void verifyNonStaged()
throws PackageManagerException {
final VerificationParams verifyingSession = prepareForVerification();
@@ -2501,19 +2622,6 @@
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Session not sealed");
}
- PackageLite result = parseApkLite();
- if (result != null) {
- mPackageLite = result;
- if (!isApexSession()) {
- synchronized (mProgressLock) {
- mInternalProgress = 0.5f;
- computeProgressLocked(true);
- }
-
- extractNativeLibraries(
- mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
- }
- }
return makeVerificationParamsLocked();
}
}
@@ -2533,82 +2641,6 @@
closeInternal(false);
}
- /**
- * Prepares staged directory with any inherited APKs and returns the parsed package.
- */
- @GuardedBy("mLock")
- @Nullable
- private PackageLite parseApkLite() throws PackageManagerException {
-
-
- if (isMultiPackage()) {
- return null;
- }
- Objects.requireNonNull(mPackageName);
- Objects.requireNonNull(mSigningDetails);
- Objects.requireNonNull(mResolvedBaseFile);
-
- // Inherit any packages and native libraries from existing install that
- // haven't been overridden.
- if (!isApexSession() && params.mode == SessionParams.MODE_INHERIT_EXISTING) {
- try {
- final List<File> fromFiles = mResolvedInheritedFiles;
- final File toDir = stageDir;
-
- if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
- if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
- throw new IllegalStateException("mInheritedFilesBase == null");
- }
-
- if (isLinkPossible(fromFiles, toDir)) {
- if (!mResolvedInstructionSets.isEmpty()) {
- final File oatDir = new File(toDir, "oat");
- createOatDirs(mResolvedInstructionSets, oatDir);
- }
- // pre-create lib dirs for linking if necessary
- if (!mResolvedNativeLibPaths.isEmpty()) {
- for (String libPath : mResolvedNativeLibPaths) {
- // "/lib/arm64" -> ["lib", "arm64"]
- final int splitIndex = libPath.lastIndexOf('/');
- if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
- Slog.e(TAG,
- "Skipping native library creation for linking due"
- + " to invalid path: " + libPath);
- continue;
- }
- final String libDirPath = libPath.substring(1, splitIndex);
- final File libDir = new File(toDir, libDirPath);
- if (!libDir.exists()) {
- NativeLibraryHelper.createNativeLibrarySubdir(libDir);
- }
- final String archDirPath = libPath.substring(splitIndex + 1);
- NativeLibraryHelper.createNativeLibrarySubdir(
- new File(libDir, archDirPath));
- }
- }
- linkFiles(fromFiles, toDir, mInheritedFilesBase);
- } else {
- // TODO: this should delegate to DCS so the system process
- // avoids holding open FDs into containers.
- copyFiles(fromFiles, toDir);
- }
- } catch (IOException e) {
- throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
- "Failed to inherit existing install", e);
- }
- }
-
- if (!isApexSession()) {
- // For mode inherit existing, it would link/copy existing files to stage dir in the
- // above block. Therefore, we need to parse the complete package in stage dir here.
- // Besides, PackageLite may be null for staged sessions that don't complete
- // pre-reboot verification.
- return getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
- } else {
- return getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0);
- }
- }
-
@GuardedBy("mLock")
@Nullable
/**
@@ -2659,7 +2691,12 @@
// staged session or not. For a staged session, we will hand it over to the staging
// manager to complete the installation.
if (isStaged()) {
- mStagingManager.commitSession(mStagedSession);
+ mSessionProvider.getSessionVerifier().verifyStaged(mStagedSession, (error, msg) -> {
+ mStagedSession.notifyEndPreRebootVerification();
+ if (error == SessionInfo.STAGED_SESSION_NO_ERROR) {
+ mStagingManager.commitSession(mStagedSession);
+ }
+ });
return;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3eaa26e..3d916ae4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -142,7 +142,6 @@
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedActivityImpl;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedMainComponent;
@@ -4875,7 +4874,7 @@
return;
}
} else {
- if (isInstantApp(packageName, callingUserId)) {
+ if (isInstantAppInternal(packageName, callingUserId, Process.SYSTEM_UID)) {
return;
}
}
@@ -4982,7 +4981,8 @@
public void reconcileSecondaryDexFiles(String packageName) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
- } else if (isInstantApp(packageName, UserHandle.getCallingUserId())) {
+ } else if (isInstantAppInternal(
+ packageName, UserHandle.getCallingUserId(), Process.SYSTEM_UID)) {
return;
}
mDexManager.reconcileSecondaryDexFiles(packageName);
@@ -6597,7 +6597,8 @@
if (DEBUG_BACKUP) {
Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH");
}
- final boolean isInstantApp = isInstantApp(packageName, userId);
+ final boolean isInstantApp = isInstantAppInternal(
+ packageName, userId, Process.SYSTEM_UID);
final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
mBroadcastHelper.sendFirstLaunchBroadcast(
@@ -7992,7 +7993,7 @@
void sendPackageChangedBroadcast(String packageName,
boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason) {
final int userId = UserHandle.getUserId(packageUid);
- final boolean isInstantApp = isInstantApp(packageName, userId);
+ final boolean isInstantApp = isInstantAppInternal(packageName, userId, Process.SYSTEM_UID);
final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
final SparseArray<int[]> broadcastAllowList = getBroadcastAllowList(
@@ -8914,7 +8915,8 @@
* return {@code true} if any one application belongs to the shared user ID meets the criteria.
*/
boolean canQueryPackage(int callingUid, @Nullable String targetPackageName) {
- if (targetPackageName == null) {
+ // Since getSettingLPr returns null for ROOT_UID, add an extra check for it here.
+ if (callingUid == Process.ROOT_UID || targetPackageName == null) {
return true;
}
synchronized (mLock) {
@@ -10006,7 +10008,7 @@
if (ps == null) {
return null;
}
- return ps.getIncrementalStatesInfo();
+ return new IncrementalStatesInfo(ps.isPackageLoading(), ps.getLoadingProgress());
}
@Override
@@ -10340,7 +10342,7 @@
throw new SecurityException(
"Caller uid " + callingUid + " does not own package " + packageName);
}
- if (isInstantAppInternal(packageName, userId, callingUid)) {
+ if (isInstantAppInternal(packageName, userId, Process.SYSTEM_UID)) {
return false;
}
final AndroidPackage pkg;
diff --git a/services/core/java/com/android/server/pm/PackageSessionProvider.java b/services/core/java/com/android/server/pm/PackageSessionProvider.java
index af11e77..ad5cf13 100644
--- a/services/core/java/com/android/server/pm/PackageSessionProvider.java
+++ b/services/core/java/com/android/server/pm/PackageSessionProvider.java
@@ -16,7 +16,10 @@
package com.android.server.pm;
-/** Provides access to individual sessions managed by the install service */
+/**
+ * Provides access to individual sessions managed by the install service as well as utilities
+ * used by the install process.
+ */
public interface PackageSessionProvider {
/**
@@ -25,4 +28,5 @@
*/
PackageInstallerSession getSession(int sessionId);
+ PackageSessionVerifier getSessionVerifier();
}
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
new file mode 100644
index 0000000..3f5410d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -0,0 +1,485 @@
+/*
+ * 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.pm;
+
+import android.apex.ApexInfo;
+import android.apex.ApexInfoList;
+import android.apex.ApexSessionInfo;
+import android.apex.ApexSessionParams;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.apk.ApkSignatureVerifier;
+
+import com.android.internal.content.PackageHelper;
+import com.android.server.LocalServices;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.rollback.RollbackManagerInternal;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+final class PackageSessionVerifier {
+ private static final String TAG = "PackageSessionVerifier";
+
+ interface Callback {
+ void onResult(int returnCode, String msg);
+ }
+
+ private final Context mContext;
+ private final PackageManagerService mPm;
+ private final ApexManager mApexManager;
+ private final Supplier<PackageParser2> mPackageParserSupplier;
+ // The handler thread to run verification tasks. Since all tasks run in the same thread,
+ // there is no need for synchronization.
+ private final Handler mHandler;
+ // Parent sessions for checking session conflicts.
+ private final List<StagingManager.StagedSession> mStagedSessions = new ArrayList<>();
+
+ PackageSessionVerifier(Context context, PackageManagerService pm,
+ ApexManager apexManager, Supplier<PackageParser2> packageParserSupplier,
+ Looper looper) {
+ mContext = context;
+ mPm = pm;
+ mApexManager = apexManager;
+ mPackageParserSupplier = packageParserSupplier;
+ mHandler = new Handler(looper);
+ }
+
+ /**
+ * Starts pre-reboot verification for the staged-session. This operation is broken into the
+ * following phases:
+ * <ul>
+ * <li>Checks if multiple active sessions are supported.</li>
+ * <li>Checks conflicts in rollbacks and overlapping packages.</li>
+ * <li>Submits apex sessions to apex service.</li>
+ * <li>Validates signatures of apex files.</li>
+ * <li>Notifies the result of verification.</li>
+ * </ul>
+ *
+ * Note it is the responsibility of the caller to ensure the staging files remain unchanged
+ * while the verification is in progress.
+ */
+ void verifyStaged(StagingManager.StagedSession session, Callback callback) {
+ Slog.d(TAG, "Starting preRebootVerification for session " + session.sessionId());
+ mHandler.post(() -> {
+ try {
+ storeSession(session);
+ checkActiveSessions();
+ checkRollbacks(session);
+ if (session.isMultiPackage()) {
+ for (StagingManager.StagedSession child : session.getChildSessions()) {
+ checkOverlaps(session, child);
+ }
+ } else {
+ checkOverlaps(session, session);
+ }
+ dispatchVerifyApex(session, callback);
+ } catch (PackageManagerException e) {
+ onVerificationFailure(session, callback, e.error, e.getMessage());
+ }
+ });
+ }
+
+ /**
+ * Stores staged-sessions for checking package overlapping and rollback conflicts.
+ */
+ private void storeSession(StagingManager.StagedSession session) {
+ mStagedSessions.add(session);
+ }
+
+ private void onVerificationSuccess(StagingManager.StagedSession session, Callback callback) {
+ callback.onResult(SessionInfo.STAGED_SESSION_NO_ERROR, null);
+ }
+
+ private void onVerificationFailure(StagingManager.StagedSession session, Callback callback,
+ @SessionInfo.StagedSessionErrorCode int errorCode, String errorMessage) {
+ if (!ensureActiveApexSessionIsAborted(session)) {
+ Slog.e(TAG, "Failed to abort apex session " + session.sessionId());
+ // Safe to ignore active apex session abortion failure since session will be marked
+ // failed on next step and staging directory for session will be deleted.
+ }
+ session.setSessionFailed(errorCode, errorMessage);
+ callback.onResult(errorCode, errorMessage);
+ }
+
+ private void dispatchVerifyApex(StagingManager.StagedSession session, Callback callback) {
+ mHandler.post(() -> {
+ try {
+ verifyApex(session);
+ dispatchEndVerification(session, callback);
+ } catch (PackageManagerException e) {
+ onVerificationFailure(session, callback, e.error, e.getMessage());
+ }
+ });
+ }
+
+ private void dispatchEndVerification(StagingManager.StagedSession session, Callback callback) {
+ mHandler.post(() -> {
+ try {
+ endVerification(session);
+ onVerificationSuccess(session, callback);
+ } catch (PackageManagerException e) {
+ onVerificationFailure(session, callback, e.error, e.getMessage());
+ }
+ });
+ }
+
+ /**
+ * Pre-reboot verification phase for apex files:
+ *
+ * <p><ul>
+ * <li>submits session to apex service</li>
+ * <li>validates signatures of apex files</li>
+ * </ul></p>
+ */
+ private void verifyApex(StagingManager.StagedSession session) throws PackageManagerException {
+ int rollbackId = -1;
+ if ((session.sessionParams().installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+ // If rollback is enabled for this session, we call through to the RollbackManager
+ // with the list of sessions it must enable rollback for. Note that
+ // notifyStagedSession is a synchronous operation.
+ final RollbackManagerInternal rm =
+ LocalServices.getService(RollbackManagerInternal.class);
+ try {
+ // NOTE: To stay consistent with the non-staged install flow, we don't fail the
+ // entire install if rollbacks can't be enabled.
+ rollbackId = rm.notifyStagedSession(session.sessionId());
+ } catch (RuntimeException re) {
+ Slog.e(TAG, "Failed to notifyStagedSession for session: "
+ + session.sessionId(), re);
+ }
+ } else if (isRollback(session)) {
+ rollbackId = retrieveRollbackIdForCommitSession(session.sessionId());
+ }
+
+ final boolean hasApex = session.containsApexSession();
+ // APEX checks. For single-package sessions, check if they contain an APEX. For
+ // multi-package sessions, find all the child sessions that contain an APEX.
+ if (hasApex) {
+ final List<PackageInfo> apexPackages = submitSessionToApexService(session, rollbackId);
+ for (int i = 0, size = apexPackages.size(); i < size; i++) {
+ validateApexSignature(apexPackages.get(i));
+ }
+ final PackageManagerInternal packageManagerInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+ packageManagerInternal.pruneCachedApksInApex(apexPackages);
+ }
+ }
+
+ /**
+ * Pre-reboot verification phase for wrapping up:
+ * <p><ul>
+ * <li>enables checkpoint if supported</li>
+ * <li>marks session as ready</li>
+ * </ul></p>
+ */
+ private void endVerification(StagingManager.StagedSession session)
+ throws PackageManagerException {
+ // Before marking the session as ready, start checkpoint service if available
+ try {
+ if (PackageHelper.getStorageManager().supportsCheckpoint()) {
+ PackageHelper.getStorageManager().startCheckpoint(2);
+ }
+ } catch (Exception e) {
+ // Failed to get hold of StorageManager
+ Slog.e(TAG, "Failed to get hold of StorageManager", e);
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_UNKNOWN,
+ "Failed to get hold of StorageManager");
+ }
+ // Proactively mark session as ready before calling apexd. Although this call order
+ // looks counter-intuitive, this is the easiest way to ensure that session won't end up
+ // in the inconsistent state:
+ // - If device gets rebooted right before call to apexd, then apexd will never activate
+ // apex files of this staged session. This will result in StagingManager failing
+ // the session.
+ // On the other hand, if the order of the calls was inverted (first call apexd, then
+ // mark session as ready), then if a device gets rebooted right after the call to apexd,
+ // only apex part of the train will be applied, leaving device in an inconsistent state.
+ Slog.d(TAG, "Marking session " + session.sessionId() + " as ready");
+ session.setSessionReady();
+ if (session.isSessionReady()) {
+ final boolean hasApex = session.containsApexSession();
+ if (hasApex) {
+ mApexManager.markStagedSessionReady(session.sessionId());
+ }
+ }
+ }
+
+ /**
+ * Validates the signature used to sign the container of the new apex package
+ *
+ * @param newApexPkg The new apex package that is being installed
+ */
+ private void validateApexSignature(PackageInfo newApexPkg) throws PackageManagerException {
+ // Get signing details of the new package
+ final String apexPath = newApexPkg.applicationInfo.sourceDir;
+ final String packageName = newApexPkg.packageName;
+ int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
+ newApexPkg.applicationInfo.targetSdkVersion);
+
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> newResult = ApkSignatureVerifier.verify(
+ input.reset(), apexPath, minSignatureScheme);
+ if (newResult.isError()) {
+ throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Failed to parse APEX package " + apexPath + " : "
+ + newResult.getException(), newResult.getException());
+ }
+ final SigningDetails newSigningDetails = newResult.getResult();
+
+ // Get signing details of the existing package
+ final PackageInfo existingApexPkg = mApexManager.getPackageInfo(packageName,
+ ApexManager.MATCH_ACTIVE_PACKAGE);
+ if (existingApexPkg == null) {
+ // This should never happen, because submitSessionToApexService ensures that no new
+ // apexes were installed.
+ throw new IllegalStateException("Unknown apex package " + packageName);
+ }
+
+ final ParseResult<SigningDetails> existingResult = ApkSignatureVerifier.verify(
+ input.reset(), existingApexPkg.applicationInfo.sourceDir,
+ SigningDetails.SignatureSchemeVersion.JAR);
+ if (existingResult.isError()) {
+ throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir
+ + " : " + existingResult.getException(), existingResult.getException());
+ }
+ final SigningDetails existingSigningDetails = existingResult.getResult();
+
+ // Verify signing details for upgrade
+ if (newSigningDetails.checkCapability(existingSigningDetails,
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
+ || existingSigningDetails.checkCapability(newSigningDetails,
+ SigningDetails.CertCapabilities.ROLLBACK)) {
+ return;
+ }
+
+ throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "APK-container signature of APEX package " + packageName + " with version "
+ + newApexPkg.versionCodeMajor + " and path " + apexPath + " is not"
+ + " compatible with the one currently installed on device");
+ }
+
+ private List<PackageInfo> submitSessionToApexService(StagingManager.StagedSession session,
+ int rollbackId) throws PackageManagerException {
+ final IntArray childSessionIds = new IntArray();
+ if (session.isMultiPackage()) {
+ for (StagingManager.StagedSession s : session.getChildSessions()) {
+ if (s.isApexSession()) {
+ childSessionIds.add(s.sessionId());
+ }
+ }
+ }
+ ApexSessionParams apexSessionParams = new ApexSessionParams();
+ apexSessionParams.sessionId = session.sessionId();
+ apexSessionParams.childSessionIds = childSessionIds.toArray();
+ if (session.sessionParams().installReason == PackageManager.INSTALL_REASON_ROLLBACK) {
+ apexSessionParams.isRollback = true;
+ apexSessionParams.rollbackId = rollbackId;
+ } else {
+ if (rollbackId != -1) {
+ apexSessionParams.hasRollbackEnabled = true;
+ apexSessionParams.rollbackId = rollbackId;
+ }
+ }
+ // submitStagedSession will throw a PackageManagerException if apexd verification fails,
+ // which will be propagated to populate stagedSessionErrorMessage of this session.
+ final ApexInfoList apexInfoList = mApexManager.submitStagedSession(apexSessionParams);
+ final List<PackageInfo> result = new ArrayList<>();
+ final List<String> apexPackageNames = new ArrayList<>();
+ for (ApexInfo apexInfo : apexInfoList.apexInfos) {
+ final PackageInfo packageInfo;
+ final int flags = PackageManager.GET_META_DATA;
+ try (PackageParser2 packageParser = mPackageParserSupplier.get()) {
+ File apexFile = new File(apexInfo.modulePath);
+ final ParsedPackage parsedPackage = packageParser.parsePackage(
+ apexFile, flags, false);
+ packageInfo = PackageInfoWithoutStateUtils.generate(parsedPackage, apexInfo, flags);
+ if (packageInfo == null) {
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Unable to generate package info: " + apexInfo.modulePath);
+ }
+ } catch (PackageManagerException e) {
+ throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
+ }
+ result.add(packageInfo);
+ apexPackageNames.add(packageInfo.packageName);
+ }
+ Slog.d(TAG, "Session " + session.sessionId() + " has following APEX packages: "
+ + apexPackageNames);
+ return result;
+ }
+
+ private int retrieveRollbackIdForCommitSession(int sessionId) throws PackageManagerException {
+ RollbackManager rm = mContext.getSystemService(RollbackManager.class);
+
+ final List<RollbackInfo> rollbacks = rm.getRecentlyCommittedRollbacks();
+ for (int i = 0, size = rollbacks.size(); i < size; i++) {
+ final RollbackInfo rollback = rollbacks.get(i);
+ if (rollback.getCommittedSessionId() == sessionId) {
+ return rollback.getRollbackId();
+ }
+ }
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Could not find rollback id for commit session: " + sessionId);
+ }
+
+ private static boolean isRollback(StagingManager.StagedSession session) {
+ return session.sessionParams().installReason == PackageManager.INSTALL_REASON_ROLLBACK;
+ }
+
+ private static boolean isApexSessionFinalized(ApexSessionInfo info) {
+ /* checking if the session is in a final state, i.e., not active anymore */
+ return info.isUnknown || info.isActivationFailed || info.isSuccess
+ || info.isReverted;
+ }
+
+ private boolean ensureActiveApexSessionIsAborted(StagingManager.StagedSession session) {
+ if (!session.containsApexSession()) {
+ return true;
+ }
+ final int sessionId = session.sessionId();
+ final ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(sessionId);
+ if (apexSession == null || isApexSessionFinalized(apexSession)) {
+ return true;
+ }
+ return mApexManager.abortStagedSession(sessionId);
+ }
+
+ /**
+ * Checks if multiple staged-sessions are supported. It is supported only when the system
+ * supports checkpoint.
+ */
+ private void checkActiveSessions() throws PackageManagerException {
+ final boolean supportsCheckpoint;
+ try {
+ supportsCheckpoint = PackageHelper.getStorageManager().supportsCheckpoint();
+ } catch (RemoteException e) {
+ throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Can't query fs-checkpoint status : " + e);
+ }
+ int activeSessions = 0;
+ for (StagingManager.StagedSession stagedSession : mStagedSessions) {
+ if (stagedSession.isDestroyed() || stagedSession.isInTerminalState()) {
+ continue;
+ }
+ ++activeSessions;
+ }
+ if (!supportsCheckpoint && activeSessions > 1) {
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Cannot stage multiple sessions without checkpoint support");
+ }
+ }
+
+ /**
+ * Fails non-rollback sessions if any rollback session exists. A rollback session might cause
+ * downgrade of SDK extension which in turn will result in dependency violation of other
+ * non-rollback sessions.
+ */
+ private void checkRollbacks(StagingManager.StagedSession session)
+ throws PackageManagerException {
+ for (StagingManager.StagedSession stagedSession : mStagedSessions) {
+ if (stagedSession.isDestroyed() || stagedSession.isInTerminalState()) {
+ continue;
+ }
+ if (isRollback(session) && !isRollback(stagedSession)) {
+ // If the new session is a rollback, then it gets priority. The existing
+ // session is failed to reduce risk and avoid an SDK extension dependency
+ // violation.
+ if (!ensureActiveApexSessionIsAborted(stagedSession)) {
+ Slog.e(TAG, "Failed to abort apex session " + stagedSession.sessionId());
+ // Safe to ignore active apex session abort failure since session
+ // will be marked failed on next step and staging directory for session
+ // will be deleted.
+ }
+ stagedSession.setSessionFailed(
+ SessionInfo.STAGED_SESSION_CONFLICT,
+ "Session was failed by rollback session: " + session.sessionId());
+ Slog.i(TAG, "Session " + stagedSession.sessionId() + " is marked failed due to "
+ + "rollback session: " + session.sessionId());
+ } else if (!isRollback(session) && isRollback(stagedSession)) {
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_CONFLICT,
+ "Session was failed by rollback session: " + stagedSession.sessionId());
+
+ }
+ }
+ }
+
+ /**
+ * Fails the session that is committed later when overlapping packages are detected.
+ *
+ * @param parent The parent session.
+ * @param child The child session whose package name will be checked.
+ * This will equal to {@code parent} for a single-package session.
+ */
+ private void checkOverlaps(StagingManager.StagedSession parent,
+ StagingManager.StagedSession child) throws PackageManagerException {
+ final String packageName = child.getPackageName();
+ if (packageName == null) {
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Cannot stage session " + child.sessionId() + " with package name null");
+ }
+ for (StagingManager.StagedSession stagedSession : mStagedSessions) {
+ if (stagedSession.isDestroyed() || stagedSession.isInTerminalState()
+ || stagedSession.sessionId() == parent.sessionId()) {
+ continue;
+ }
+ if (stagedSession.sessionContains(s -> packageName.equals(s.getPackageName()))) {
+ if (stagedSession.getCommittedMillis() < parent.getCommittedMillis()) {
+ // Fail the session committed later when there are overlapping packages
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Package: " + packageName + " in session: "
+ + child.sessionId()
+ + " has been staged already by session: "
+ + stagedSession.sessionId());
+ } else {
+ stagedSession.setSessionFailed(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Package: " + packageName + " in session: "
+ + stagedSession.sessionId()
+ + " has been staged already by session: "
+ + child.sessionId());
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 1a5415c..b8e354e 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -25,7 +25,6 @@
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IncrementalStatesInfo;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningDetails;
@@ -36,7 +35,6 @@
import android.content.pm.pkg.PackageUserState;
import android.content.pm.pkg.PackageUserStateInternal;
import android.os.PersistableBundle;
-import android.os.incremental.IncrementalManager;
import android.service.pm.PackageProto;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -66,8 +64,8 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.function.Predicate;
import java.util.UUID;
+import java.util.function.Predicate;
/**
* Settings data for a particular package we know about.
@@ -103,9 +101,6 @@
@Nullable
Set<String> mOldCodePaths;
- @NonNull
- private IncrementalStates incrementalStates;
-
@Nullable
String[] usesStaticLibraries;
@@ -153,6 +148,8 @@
@NonNull
private String mPathString;
+ private float mLoadingProgress;
+
@Nullable
private String mPrimaryCpuAbi;
@@ -183,6 +180,7 @@
// TODO: Access is not locked.
// Whether this package is currently stopped, thus can not be
// started until explicitly launched by the user.
+ @NonNull
private final SparseArray<PackageUserStateInternalImpl> mUserState = new SparseArray<>();
@NonNull
@@ -242,7 +240,6 @@
this.versionCode = longVersionCode;
this.signatures = new PackageSignatures();
this.installSource = InstallSource.EMPTY;
- this.incrementalStates = new IncrementalStates();
this.sharedUserId = sharedUserId;
mDomainSetId = domainSetId;
copyMimeGroups(mimeGroups);
@@ -614,7 +611,7 @@
super.copySettingBase(other);
sharedUserId = other.sharedUserId;
mimeGroups = other.mimeGroups;
- incrementalStates = other.incrementalStates;
+ mLoadingProgress = other.mLoadingProgress;
legacyNativeLibraryPath = other.legacyNativeLibraryPath;
mName = other.mName;
mRealName = other.mRealName;
@@ -708,6 +705,7 @@
void setInstalled(boolean inst, int userId) {
modifyUserState(userId).setInstalled(inst);
+ onChanged();
}
boolean getInstalled(int userId) {
@@ -720,6 +718,7 @@
void setInstallReason(int installReason, int userId) {
modifyUserState(userId).setInstallReason(installReason);
+ onChanged();
}
int getUninstallReason(int userId) {
@@ -728,10 +727,15 @@
void setUninstallReason(@PackageManager.UninstallReason int uninstallReason, int userId) {
modifyUserState(userId).setUninstallReason(uninstallReason);
+ onChanged();
}
boolean setOverlayPaths(OverlayPaths overlayPaths, int userId) {
- return modifyUserState(userId).setOverlayPaths(overlayPaths);
+ boolean changed = modifyUserState(userId).setOverlayPaths(overlayPaths);
+ if (changed) {
+ onChanged();
+ }
+ return changed;
}
@NonNull
@@ -740,7 +744,10 @@
}
boolean setOverlayPathsForLibrary(String libName, OverlayPaths overlayPaths, int userId) {
- return modifyUserState(userId).setSharedLibraryOverlayPaths(libName, overlayPaths);
+ boolean changed = modifyUserState(userId)
+ .setSharedLibraryOverlayPaths(libName, overlayPaths);
+ onChanged();
+ return changed;
}
@NonNull
@@ -787,6 +794,7 @@
void setCeDataInode(long ceDataInode, int userId) {
modifyUserState(userId).setCeDataInode(ceDataInode);
+ onChanged();
}
boolean getStopped(int userId) {
@@ -795,6 +803,7 @@
void setStopped(boolean stop, int userId) {
modifyUserState(userId).setStopped(stop);
+ onChanged();
}
boolean getNotLaunched(int userId) {
@@ -803,6 +812,7 @@
void setNotLaunched(boolean stop, int userId) {
modifyUserState(userId).setNotLaunched(stop);
+ onChanged();
}
boolean getHidden(int userId) {
@@ -811,6 +821,7 @@
void setHidden(boolean hidden, int userId) {
modifyUserState(userId).setHidden(hidden);
+ onChanged();
}
int getDistractionFlags(int userId) {
@@ -819,6 +830,7 @@
void setDistractionFlags(int distractionFlags, int userId) {
modifyUserState(userId).setDistractionFlags(distractionFlags);
+ onChanged();
}
boolean getSuspended(int userId) {
@@ -886,6 +898,7 @@
void setInstantApp(boolean instantApp, int userId) {
modifyUserState(userId).setInstantApp(instantApp);
+ onChanged();
}
boolean getVirtualPreload(int userId) {
@@ -894,6 +907,7 @@
void setVirtualPreload(boolean virtualPreload, int userId) {
modifyUserState(userId).setVirtualPreload(virtualPreload);
+ onChanged();
}
void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
@@ -948,20 +962,24 @@
void setEnabledComponents(ArraySet<String> components, int userId) {
modifyUserState(userId).setEnabledComponents(components);
+ onChanged();
}
void setDisabledComponents(ArraySet<String> components, int userId) {
modifyUserState(userId).setDisabledComponents(components);
+ onChanged();
}
void setEnabledComponentsCopy(ArraySet<String> components, int userId) {
modifyUserState(userId).setEnabledComponents(components != null
? new ArraySet<String>(components) : null);
+ onChanged();
}
void setDisabledComponentsCopy(ArraySet<String> components, int userId) {
modifyUserState(userId).setDisabledComponents(components != null
? new ArraySet<String>(components) : null);
+ onChanged();
}
PackageUserStateInternalImpl modifyUserStateComponents(int userId, boolean disabled,
@@ -985,11 +1003,13 @@
void addDisabledComponent(String componentClassName, int userId) {
modifyUserStateComponents(userId, true, false)
.getDisabledComponentsNoCopy().add(componentClassName);
+ onChanged();
}
void addEnabledComponent(String componentClassName, int userId) {
modifyUserStateComponents(userId, false, true)
.getEnabledComponentsNoCopy().add(componentClassName);
+ onChanged();
}
boolean enableComponentLPw(String componentClassName, int userId) {
@@ -997,6 +1017,9 @@
boolean changed = state.getDisabledComponentsNoCopy() != null
? state.getDisabledComponentsNoCopy().remove(componentClassName) : false;
changed |= state.getEnabledComponentsNoCopy().add(componentClassName);
+ if (changed) {
+ onChanged();
+ }
return changed;
}
@@ -1005,6 +1028,9 @@
boolean changed = state.getEnabledComponentsNoCopy() != null
? state.getEnabledComponentsNoCopy().remove(componentClassName) : false;
changed |= state.getDisabledComponentsNoCopy().add(componentClassName);
+ if (changed) {
+ onChanged();
+ }
return changed;
}
@@ -1014,6 +1040,9 @@
? state.getDisabledComponentsNoCopy().remove(componentClassName) : false;
changed |= state.getEnabledComponentsNoCopy() != null
? state.getEnabledComponentsNoCopy().remove(componentClassName) : false;
+ if (changed) {
+ onChanged();
+ }
return changed;
}
@@ -1131,6 +1160,7 @@
PackageSetting setPath(@NonNull File path) {
this.mPath = path;
this.mPathString = path.toString();
+ onChanged();
return this;
}
@@ -1147,7 +1177,9 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public boolean overrideNonLocalizedLabelAndIcon(@NonNull ComponentName component,
@Nullable String label, @Nullable Integer icon, @UserIdInt int userId) {
- return modifyUserState(userId).overrideLabelAndIcon(component, label, icon);
+ boolean changed = modifyUserState(userId).overrideLabelAndIcon(component, label, icon);
+ onChanged();
+ return changed;
}
/**
@@ -1157,6 +1189,7 @@
*/
public void resetOverrideComponentLabelIcon(@UserIdInt int userId) {
modifyUserState(userId).resetOverrideComponentLabelIcon();
+ onChanged();
}
/**
@@ -1184,39 +1217,13 @@
* @return True if package is still being loaded, false if the package is fully loaded.
*/
public boolean isPackageLoading() {
- return incrementalStates.getIncrementalStatesInfo().isLoading();
+ return Math.abs(1.0f - mLoadingProgress) >= 0.00000001f;
}
- /**
- * @return all current states in a Parcelable.
- */
- public IncrementalStatesInfo getIncrementalStatesInfo() {
- return incrementalStates.getIncrementalStatesInfo();
- }
-
- /**
- * Called to indicate that the package installation has been committed. This will create a
- * new startable state and a new loading state with default values. By default, the package is
- * startable after commit. For a package installed on Incremental, the loading state is true.
- * For non-Incremental packages, the loading state is false.
- */
- public void setStatesOnCommit() {
- incrementalStates.onCommit(IncrementalManager.isIncrementalPath(getPathString()));
- }
-
- /**
- * Called to set the callback to listen for startable state changes.
- */
- public void setIncrementalStatesCallback(IncrementalStates.Callback callback) {
- incrementalStates.setCallback(callback);
- }
-
- /**
- * Called to report progress changes. This might trigger loading state change.
- * @see IncrementalStates#setProgress(float)
- */
- public void setLoadingProgress(float progress) {
- incrementalStates.setProgress(progress);
+ public PackageSetting setLoadingProgress(float progress) {
+ mLoadingProgress = progress;
+ onChanged();
+ return this;
}
@NonNull
@@ -1342,13 +1349,6 @@
return this;
}
- public PackageSetting setIncrementalStates(
- IncrementalStates incrementalStates) {
- this.incrementalStates = incrementalStates;
- onChanged();
- return this;
- }
-
// Code below generated by codegen v1.0.23.
@@ -1377,11 +1377,6 @@
return mOldCodePaths;
}
- @DataClass.Generated.Member
- public @NonNull IncrementalStates getIncrementalStates() {
- return incrementalStates;
- }
-
/**
* The path under which native libraries have been unpacked. This path is
* always derived at runtime, and is only stored here for cleanup when a
@@ -1438,6 +1433,11 @@
}
@DataClass.Generated.Member
+ public float getLoadingProgress() {
+ return mLoadingProgress;
+ }
+
+ @DataClass.Generated.Member
public @Nullable String getPrimaryCpuAbi() {
return mPrimaryCpuAbi;
}
@@ -1537,10 +1537,10 @@
}
@DataClass.Generated(
- time = 1628017546382L,
+ time = 1635295317317L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- inputSignatures = "static final android.content.pm.PackageUserState DEFAULT_USER_STATE\nprotected int sharedUserId\n @android.annotation.Nullable java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>> mimeGroups\n @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.NonNull com.android.server.pm.IncrementalStates incrementalStates\n @android.annotation.Nullable java.lang.String[] usesStaticLibraries\n @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long firstInstallTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<android.content.pm.PackageUserState> mUserState\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic java.util.List<java.lang.String> getMimeGroup(java.lang.String)\nprivate android.util.ArraySet<java.lang.String> getMimeGroupInternal(java.lang.String)\npublic boolean isMatch(int)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyFrom(com.android.server.pm.PackageSetting)\nprivate void doCopy(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting android.content.pm.PackageUserState modifyUserState(int)\npublic @android.annotation.NonNull android.content.pm.PackageUserState readUserState(int)\npublic @android.annotation.Nullable android.content.pm.PackageUserState readUserStateNullable(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n @com.android.internal.annotations.VisibleForTesting android.util.SparseArray<android.content.pm.PackageUserState> getUserState()\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\n boolean getSuspended(int)\n boolean isSuspendedBy(java.lang.String,int)\n void addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n void removeSuspension(java.lang.String,int)\n void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,boolean,android.util.ArrayMap<java.lang.String,android.content.pm.PackageUserState.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n void setUserState(int,android.content.pm.PackageUserState)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n android.content.pm.PackageUserState modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\n java.lang.String getPathString()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isPackageLoading()\npublic android.content.pm.IncrementalStatesInfo getIncrementalStatesInfo()\npublic void setStatesOnCommit()\npublic void setIncrementalStatesCallback(com.android.server.pm.IncrementalStates.Callback)\npublic void setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getLongVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfoOverride()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Deprecated @java.lang.Override int[] getUserIds()\npublic @java.lang.Override android.content.pm.PackageUserState getUserState(int)\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setIncrementalStates(com.android.server.pm.IncrementalStates)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageState]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "protected int sharedUserId\n @android.annotation.Nullable java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>> mimeGroups\n @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\n @android.annotation.Nullable java.lang.String[] usesStaticLibraries\n @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float loadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long firstInstallTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateInternalImpl> mUserState\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic java.util.List<java.lang.String> getMimeGroup(java.lang.String)\nprivate android.util.ArraySet<java.lang.String> getMimeGroupInternal(java.lang.String)\npublic boolean isMatch(int)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateInternalImpl modifyUserState(int)\npublic @android.annotation.NonNull android.content.pm.pkg.PackageUserStateInternal readUserState(int)\npublic @android.annotation.Nullable android.content.pm.pkg.PackageUserState readUserStateNullable(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n @com.android.internal.annotations.VisibleForTesting android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateInternalImpl> getUserState()\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\n boolean getSuspended(int)\n boolean isSuspendedBy(java.lang.String,int)\n boolean addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n boolean removeSuspension(java.lang.String,int)\n void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,boolean,android.util.ArrayMap<java.lang.String,android.content.pm.pkg.PackageUserState.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n void setUserState(int,android.content.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateInternalImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\n java.lang.String getPathString()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isPackageLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getLongVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfoOverride()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Deprecated @java.lang.Override int[] getUserIds()\npublic @java.lang.Override android.content.pm.pkg.PackageUserState getUserState(int)\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageState]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
index 8018011..731c6ca 100644
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ b/services/core/java/com/android/server/pm/ScanPackageHelper.java
@@ -1018,9 +1018,6 @@
if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
if (pkgSetting != null && pkgSetting.isPackageLoading()) {
// Continue monitoring loading progress of active incremental packages
- final IncrementalStatesCallback incrementalStatesCallback =
- new IncrementalStatesCallback(parsedPackage.getPackageName(), mPm);
- pkgSetting.setIncrementalStatesCallback(incrementalStatesCallback);
mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f89d9eb..6df1006 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -73,7 +73,6 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.service.pm.PackageServiceDumpProto;
@@ -1087,9 +1086,6 @@
pkgSetting.setLegacyNativeLibraryPath(legacyNativeLibraryPath);
}
pkgSetting.setPath(codePath);
- if (IncrementalManager.isIncrementalPath(codePath.getAbsolutePath())) {
- pkgSetting.setIncrementalStates(new IncrementalStates());
- }
}
// If what we are scanning is a system (and possibly privileged) package,
// then make it so, regardless of whether it was previously installed only
@@ -2708,8 +2704,7 @@
} else {
serializer.attributeInt(null, "sharedUserId", pkg.getAppId());
}
- serializer.attributeFloat(null, "loadingProgress",
- pkg.getIncrementalStates().getIncrementalStatesInfo().getProgress());
+ serializer.attributeFloat(null, "loadingProgress", pkg.getLoadingProgress());
writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
@@ -2784,8 +2779,7 @@
if (pkg.isPackageLoading()) {
serializer.attributeBoolean(null, "isLoading", true);
}
- serializer.attributeFloat(null, "loadingProgress",
- pkg.getIncrementalStates().getIncrementalStatesInfo().getProgress());
+ serializer.attributeFloat(null, "loadingProgress", pkg.getLoadingProgress());
serializer.attribute(null, "domainSetId", pkg.getDomainSetId().toString());
@@ -3531,7 +3525,6 @@
PackageSetting packageSetting = null;
long versionCode = 0;
boolean installedForceQueryable = false;
- boolean isLoading = false;
float loadingProgress = 0;
UUID domainSetId;
try {
@@ -3549,7 +3542,6 @@
cpuAbiOverrideString = parser.getAttributeValue(null, "cpuAbiOverride");
updateAvailable = parser.getAttributeBoolean(null, "updateAvailable", false);
installedForceQueryable = parser.getAttributeBoolean(null, "forceQueryable", false);
- isLoading = parser.getAttributeBoolean(null, "isLoading", false);
loadingProgress = parser.getAttributeFloat(null, "loadingProgress", 0);
if (primaryCpuAbiString == null && legacyCpuAbiString != null) {
@@ -3706,7 +3698,7 @@
.setSecondaryCpuAbi(secondaryCpuAbiString)
.setUpdateAvailable(updateAvailable)
.setForceQueryableOverride(installedForceQueryable)
- .setIncrementalStates(new IncrementalStates(isLoading, loadingProgress));
+ .setLoadingProgress(loadingProgress);
// Handle legacy string here for single-user mode
final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
if (enabledStr != null) {
@@ -4681,8 +4673,7 @@
}
if (ps.isPackageLoading()) {
pw.print(prefix); pw.println(" loadingProgress=" +
- (int) (ps.getIncrementalStates().getIncrementalStatesInfo().getProgress()
- * 100) + "%");
+ (int) (ps.getLoadingProgress() * 100) + "%");
}
if (ps.getVolumeUuid() != null) {
pw.print(prefix); pw.print(" volumeUuid=");
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index a2f48c7..c83cdb4 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.apex.ApexInfo;
-import android.apex.ApexInfoList;
import android.apex.ApexSessionInfo;
import android.apex.ApexSessionParams;
import android.content.BroadcastReceiver;
@@ -31,25 +30,14 @@
import android.content.IntentSender;
import android.content.pm.ApexStagedEvent;
import android.content.pm.IStagedApexObserver;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.SigningDetails;
-import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.content.pm.StagedApexInfo;
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
-import android.content.rollback.RollbackInfo;
-import android.content.rollback.RollbackManager;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -62,7 +50,6 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimingsTraceLog;
-import android.util.apk.ApkSignatureVerifier;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -72,10 +59,8 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.rollback.RollbackManagerInternal;
import com.android.server.rollback.WatchdogRollbackLogger;
@@ -93,7 +78,6 @@
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
-import java.util.function.Supplier;
/**
* This class handles staged install sessions, i.e. install sessions that require packages to
@@ -106,9 +90,6 @@
private final ApexManager mApexManager;
private final PowerManager mPowerManager;
private final Context mContext;
- @VisibleForTesting
- final PreRebootVerificationHandler mPreRebootVerificationHandler;
- private final Supplier<PackageParser2> mPackageParserSupplier;
private final File mFailureReasonFile = new File("/metadata/staged-install/failure_reason.txt");
private String mFailureReason;
@@ -152,28 +133,19 @@
boolean hasParentSessionId();
long getCommittedMillis();
void abandon();
- void notifyEndPreRebootVerification();
void verifySession();
}
- StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier, Looper looper) {
- this(context, packageParserSupplier, ApexManager.getInstance(), looper);
+ StagingManager(Context context) {
+ this(context, ApexManager.getInstance());
}
@VisibleForTesting
- StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier,
- ApexManager apexManager) {
- this(context, packageParserSupplier, apexManager, BackgroundThread.get().getLooper());
- }
-
- StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier,
- ApexManager apexManager, Looper looper) {
+ StagingManager(Context context, ApexManager apexManager) {
mContext = context;
- mPackageParserSupplier = packageParserSupplier;
mApexManager = apexManager;
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mPreRebootVerificationHandler = new PreRebootVerificationHandler(looper);
if (mFailureReasonFile.exists()) {
try (BufferedReader reader = new BufferedReader(new FileReader(mFailureReasonFile))) {
@@ -244,128 +216,6 @@
}
}
- /**
- * Validates the signature used to sign the container of the new apex package
- *
- * @param newApexPkg The new apex package that is being installed
- */
- private void validateApexSignature(PackageInfo newApexPkg)
- throws PackageManagerException {
- // Get signing details of the new package
- final String apexPath = newApexPkg.applicationInfo.sourceDir;
- final String packageName = newApexPkg.packageName;
- int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
- newApexPkg.applicationInfo.targetSdkVersion);
-
- final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- final ParseResult<SigningDetails> newResult = ApkSignatureVerifier.verify(
- input.reset(), apexPath, minSignatureScheme);
- if (newResult.isError()) {
- throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Failed to parse APEX package " + apexPath + " : "
- + newResult.getException(), newResult.getException());
- }
- final SigningDetails newSigningDetails = newResult.getResult();
-
- // Get signing details of the existing package
- final PackageInfo existingApexPkg = mApexManager.getPackageInfo(packageName,
- ApexManager.MATCH_ACTIVE_PACKAGE);
- if (existingApexPkg == null) {
- // This should never happen, because submitSessionToApexService ensures that no new
- // apexes were installed.
- throw new IllegalStateException("Unknown apex package " + packageName);
- }
-
- final ParseResult<SigningDetails> existingResult = ApkSignatureVerifier.verify(
- input.reset(), existingApexPkg.applicationInfo.sourceDir,
- SignatureSchemeVersion.JAR);
- if (existingResult.isError()) {
- throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir
- + " : " + existingResult.getException(), existingResult.getException());
- }
- final SigningDetails existingSigningDetails = existingResult.getResult();
-
- // Verify signing details for upgrade
- if (newSigningDetails.checkCapability(existingSigningDetails,
- SigningDetails.CertCapabilities.INSTALLED_DATA)
- || existingSigningDetails.checkCapability(newSigningDetails,
- SigningDetails.CertCapabilities.ROLLBACK)) {
- return;
- }
-
- throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "APK-container signature of APEX package " + packageName + " with version "
- + newApexPkg.versionCodeMajor + " and path " + apexPath + " is not"
- + " compatible with the one currently installed on device");
- }
-
- private List<PackageInfo> submitSessionToApexService(@NonNull StagedSession session,
- int rollbackId) throws PackageManagerException {
- final IntArray childSessionIds = new IntArray();
- if (session.isMultiPackage()) {
- for (StagedSession s : session.getChildSessions()) {
- if (s.isApexSession()) {
- childSessionIds.add(s.sessionId());
- }
- }
- }
- ApexSessionParams apexSessionParams = new ApexSessionParams();
- apexSessionParams.sessionId = session.sessionId();
- apexSessionParams.childSessionIds = childSessionIds.toArray();
- if (session.sessionParams().installReason == PackageManager.INSTALL_REASON_ROLLBACK) {
- apexSessionParams.isRollback = true;
- apexSessionParams.rollbackId = rollbackId;
- } else {
- if (rollbackId != -1) {
- apexSessionParams.hasRollbackEnabled = true;
- apexSessionParams.rollbackId = rollbackId;
- }
- }
- // submitStagedSession will throw a PackageManagerException if apexd verification fails,
- // which will be propagated to populate stagedSessionErrorMessage of this session.
- final ApexInfoList apexInfoList = mApexManager.submitStagedSession(apexSessionParams);
- final List<PackageInfo> result = new ArrayList<>();
- final List<String> apexPackageNames = new ArrayList<>();
- for (ApexInfo apexInfo : apexInfoList.apexInfos) {
- final PackageInfo packageInfo;
- final int flags = PackageManager.GET_META_DATA;
- try (PackageParser2 packageParser = mPackageParserSupplier.get()) {
- File apexFile = new File(apexInfo.modulePath);
- final ParsedPackage parsedPackage = packageParser.parsePackage(
- apexFile, flags, false);
- packageInfo = PackageInfoWithoutStateUtils.generate(parsedPackage, apexInfo, flags);
- if (packageInfo == null) {
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Unable to generate package info: " + apexInfo.modulePath);
- }
- } catch (PackageManagerException e) {
- throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
- }
- result.add(packageInfo);
- apexPackageNames.add(packageInfo.packageName);
- }
- Slog.d(TAG, "Session " + session.sessionId() + " has following APEX packages: "
- + apexPackageNames);
- return result;
- }
-
- private int retrieveRollbackIdForCommitSession(int sessionId) throws PackageManagerException {
- RollbackManager rm = mContext.getSystemService(RollbackManager.class);
-
- final List<RollbackInfo> rollbacks = rm.getRecentlyCommittedRollbacks();
- for (int i = 0, size = rollbacks.size(); i < size; i++) {
- final RollbackInfo rollback = rollbacks.get(i);
- if (rollback.getCommittedSessionId() == sessionId) {
- return rollback.getRollbackId();
- }
- }
- throw new PackageManagerException(
- "Could not find rollback id for commit session: " + sessionId);
- }
-
// Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
private void abortCheckpoint(String failureReason, boolean supportsCheckpoint,
boolean needsCheckpoint) {
@@ -663,129 +513,15 @@
}
}
- void commitSession(@NonNull StagedSession session) {
- // Store this parent session which will be used to check overlapping later
- createSession(session);
- mPreRebootVerificationHandler.startPreRebootVerification(session);
- }
-
- private int getSessionIdForParentOrSelf(StagedSession session) {
- return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId();
- }
-
- private StagedSession getParentSessionOrSelf(StagedSession session) {
- return session.hasParentSessionId()
- ? getStagedSession(session.getParentSessionId())
- : session;
- }
-
- private boolean isRollback(StagedSession session) {
- final StagedSession root = getParentSessionOrSelf(session);
- return root.sessionParams().installReason == PackageManager.INSTALL_REASON_ROLLBACK;
- }
-
- /**
- * <p> Check if the session provided is non-overlapping with the active staged sessions.
- *
- * <p> A session is non-overlapping if it meets one of the following conditions: </p>
- * <ul>
- * <li>It is a parent session</li>
- * <li>It is already one of the active sessions</li>
- * <li>Its package name is not same as any of the active sessions</li>
- * </ul>
- * @throws PackageManagerException if session fails the check
- */
- // TODO(b/192625695): Rename this method which checks rollbacks in addition to overlapping
@VisibleForTesting
- void checkNonOverlappingWithStagedSessions(@NonNull StagedSession session)
- throws PackageManagerException {
- if (session.isMultiPackage()) {
- // We cannot say a parent session overlaps until we process its children
- return;
- }
+ void commitSession(@NonNull StagedSession session) {
+ createSession(session);
+ handleCommittedSession(session);
+ }
- String packageName = session.getPackageName();
- if (packageName == null) {
- throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Cannot stage session " + session.sessionId() + " with package name null");
- }
-
- boolean supportsCheckpoint;
- try {
- supportsCheckpoint = PackageHelper.getStorageManager().supportsCheckpoint();
- } catch (RemoteException e) {
- throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Can't query fs-checkpoint status : " + e);
- }
-
- final boolean isRollback = isRollback(session);
-
- synchronized (mStagedSessions) {
- for (int i = 0; i < mStagedSessions.size(); i++) {
- final StagedSession stagedSession = mStagedSessions.valueAt(i);
- if (stagedSession.hasParentSessionId() || !stagedSession.isCommitted()
- || stagedSession.isInTerminalState()
- || stagedSession.isDestroyed()) {
- continue;
- }
-
- // From here on, stagedSession is a parent active staged session
-
- // Check if session is one of the active sessions
- if (getSessionIdForParentOrSelf(session) == stagedSession.sessionId()) {
- Slog.w(TAG, "Session " + session.sessionId() + " is already staged");
- continue;
- }
-
- if (isRollback && !isRollback(stagedSession)) {
- // If the new session is a rollback, then it gets priority. The existing
- // session is failed to reduce risk and avoid an SDK extension dependency
- // violation.
- final StagedSession root = stagedSession;
- if (!ensureActiveApexSessionIsAborted(root)) {
- Slog.e(TAG, "Failed to abort apex session " + root.sessionId());
- // Safe to ignore active apex session abort failure since session
- // will be marked failed on next step and staging directory for session
- // will be deleted.
- }
- root.setSessionFailed(
- SessionInfo.STAGED_SESSION_CONFLICT,
- "Session was failed by rollback session: " + session.sessionId());
- Slog.i(TAG, "Session " + root.sessionId() + " is marked failed due to "
- + "rollback session: " + session.sessionId());
- } else if (!isRollback && isRollback(stagedSession)) {
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_CONFLICT,
- "Session was failed by rollback session: " + stagedSession.sessionId());
- } else if (stagedSession.sessionContains(
- s -> s.getPackageName().equals(packageName))) {
- // Fail the session committed later when there are overlapping packages
- if (stagedSession.getCommittedMillis() < session.getCommittedMillis()) {
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Package: " + session.getPackageName() + " in session: "
- + session.sessionId()
- + " has been staged already by session: "
- + stagedSession.sessionId(), null);
- } else {
- stagedSession.setSessionFailed(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Package: " + packageName + " in session: "
- + stagedSession.sessionId()
- + " has been staged already by session: "
- + session.sessionId());
- }
- }
-
- // Staging multiple root sessions is not allowed if device doesn't support
- // checkpoint. If session and stagedSession do not have common ancestor, they are
- // from two different root sessions.
- if (!supportsCheckpoint) {
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Cannot stage multiple sessions without checkpoint support", null);
- }
- }
+ private void handleCommittedSession(@NonNull StagedSession session) {
+ if (session.isSessionReady() && session.containsApexSession()) {
+ notifyStagedApexObservers();
}
}
@@ -1234,237 +970,4 @@
}
}
}
-
- @VisibleForTesting
- final class PreRebootVerificationHandler extends Handler {
-
- PreRebootVerificationHandler(Looper looper) {
- super(looper);
- }
-
- /**
- * Handler for states of pre reboot verification. The states are arranged linearly (shown
- * below) with each state either calling the next state, or calling some other method that
- * eventually calls the next state.
- *
- * <p><ul>
- * <li>MSG_PRE_REBOOT_VERIFICATION_START</li>
- * <li>MSG_PRE_REBOOT_VERIFICATION_APEX</li>
- * <li>MSG_PRE_REBOOT_VERIFICATION_END</li>
- * </ul></p>
- *
- * Details about each of state can be found in corresponding handler of node.
- */
- private static final int MSG_PRE_REBOOT_VERIFICATION_START = 1;
- private static final int MSG_PRE_REBOOT_VERIFICATION_APEX = 2;
- @VisibleForTesting
- static final int MSG_PRE_REBOOT_VERIFICATION_END = 3;
-
- @Override
- public void handleMessage(Message msg) {
- final int sessionId = msg.arg1;
- final int rollbackId = msg.arg2;
- final StagedSession session = (StagedSession) msg.obj;
- if (session.isDestroyed() || session.isSessionFailed()) {
- // No point in running verification on a destroyed/failed session
- onPreRebootVerificationComplete(session);
- return;
- }
- try {
- switch (msg.what) {
- case MSG_PRE_REBOOT_VERIFICATION_START:
- handlePreRebootVerification_Start(session);
- break;
- case MSG_PRE_REBOOT_VERIFICATION_APEX:
- handlePreRebootVerification_Apex(session, rollbackId);
- break;
- case MSG_PRE_REBOOT_VERIFICATION_END:
- handlePreRebootVerification_End(session);
- break;
- }
- } catch (Exception e) {
- Slog.e(TAG, "Pre-reboot verification failed due to unhandled exception", e);
- onPreRebootVerificationFailure(session,
- SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
- "Pre-reboot verification failed due to unhandled exception: " + e);
- }
- }
-
- // Method for starting the pre-reboot verification
- private synchronized void startPreRebootVerification(
- @NonNull StagedSession session) {
- mBootCompleted.thenRun(() -> {
- int sessionId = session.sessionId();
- Slog.d(TAG, "Starting preRebootVerification for session " + sessionId);
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, -1, session)
- .sendToTarget();
- });
- }
-
- private void onPreRebootVerificationFailure(StagedSession session,
- @SessionInfo.StagedSessionErrorCode int errorCode, String errorMessage) {
- if (!ensureActiveApexSessionIsAborted(session)) {
- Slog.e(TAG, "Failed to abort apex session " + session.sessionId());
- // Safe to ignore active apex session abortion failure since session will be marked
- // failed on next step and staging directory for session will be deleted.
- }
- session.setSessionFailed(errorCode, errorMessage);
- onPreRebootVerificationComplete(session);
- }
-
- // Things to do when pre-reboot verification completes for a particular sessionId
- private void onPreRebootVerificationComplete(StagedSession session) {
- int sessionId = session.sessionId();
- Slog.d(TAG, "Stopping preRebootVerification for session " + sessionId);
- session.notifyEndPreRebootVerification();
- }
-
- private void notifyPreRebootVerification_Start_Complete(
- @NonNull StagedSession session, int rollbackId) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, session.sessionId(), rollbackId,
- session).sendToTarget();
- }
-
- private void notifyPreRebootVerification_Apex_Complete(
- @NonNull StagedSession session) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId(), -1, session)
- .sendToTarget();
- }
-
- /**
- * A placeholder state for starting the pre reboot verification.
- *
- * See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification
- */
- private void handlePreRebootVerification_Start(@NonNull StagedSession session) {
- try {
- if (session.isMultiPackage()) {
- for (StagedSession s : session.getChildSessions()) {
- checkNonOverlappingWithStagedSessions(s);
- }
- } else {
- checkNonOverlappingWithStagedSessions(session);
- }
- } catch (PackageManagerException e) {
- onPreRebootVerificationFailure(session, e.error, e.getMessage());
- return;
- }
-
- int rollbackId = -1;
- if ((session.sessionParams().installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK)
- != 0) {
- // If rollback is enabled for this session, we call through to the RollbackManager
- // with the list of sessions it must enable rollback for. Note that
- // notifyStagedSession is a synchronous operation.
- final RollbackManagerInternal rm =
- LocalServices.getService(RollbackManagerInternal.class);
- try {
- // NOTE: To stay consistent with the non-staged install flow, we don't fail the
- // entire install if rollbacks can't be enabled.
- rollbackId = rm.notifyStagedSession(session.sessionId());
- } catch (RuntimeException re) {
- Slog.e(TAG, "Failed to notifyStagedSession for session: "
- + session.sessionId(), re);
- }
- } else if (session.sessionParams().installReason
- == PackageManager.INSTALL_REASON_ROLLBACK) {
- try {
- rollbackId = retrieveRollbackIdForCommitSession(session.sessionId());
- } catch (PackageManagerException e) {
- onPreRebootVerificationFailure(session, e.error, e.getMessage());
- return;
- }
- }
-
- notifyPreRebootVerification_Start_Complete(session, rollbackId);
- }
-
- /**
- * Pre-reboot verification state for apex files:
- *
- * <p><ul>
- * <li>submits session to apex service</li>
- * <li>validates signatures of apex files</li>
- * </ul></p>
- */
- private void handlePreRebootVerification_Apex(
- @NonNull StagedSession session, int rollbackId) {
- final boolean hasApex = session.containsApexSession();
-
- // APEX checks. For single-package sessions, check if they contain an APEX. For
- // multi-package sessions, find all the child sessions that contain an APEX.
- if (hasApex) {
- final List<PackageInfo> apexPackages;
- try {
- apexPackages = submitSessionToApexService(session, rollbackId);
- for (int i = 0, size = apexPackages.size(); i < size; i++) {
- validateApexSignature(apexPackages.get(i));
- }
- } catch (PackageManagerException e) {
- onPreRebootVerificationFailure(session, e.error, e.getMessage());
- return;
- }
-
- final PackageManagerInternal packageManagerInternal =
- LocalServices.getService(PackageManagerInternal.class);
- packageManagerInternal.pruneCachedApksInApex(apexPackages);
- }
-
- notifyPreRebootVerification_Apex_Complete(session);
- }
-
- /**
- * Pre-reboot verification state for wrapping up:
- * <p><ul>
- * <li>enables rollback if required</li>
- * <li>marks session as ready</li>
- * </ul></p>
- */
- private void handlePreRebootVerification_End(@NonNull StagedSession session) {
- // Before marking the session as ready, start checkpoint service if available
- try {
- if (PackageHelper.getStorageManager().supportsCheckpoint()) {
- PackageHelper.getStorageManager().startCheckpoint(2);
- }
- } catch (Exception e) {
- // Failed to get hold of StorageManager
- Slog.e(TAG, "Failed to get hold of StorageManager", e);
- onPreRebootVerificationFailure(session, SessionInfo.STAGED_SESSION_UNKNOWN,
- "Failed to get hold of StorageManager");
- return;
- }
-
- // Stop pre-reboot verification before marking session ready. From this point on, if we
- // abandon the session then it will be cleaned up immediately. If session is abandoned
- // after this point, then even if for some reason system tries to install the session
- // or activate its apex, there won't be any files to work with as they will be cleaned
- // up by the system as part of abandonment. If session is abandoned before this point,
- // then the session is already destroyed and cannot be marked ready anymore.
- onPreRebootVerificationComplete(session);
-
- // Proactively mark session as ready before calling apexd. Although this call order
- // looks counter-intuitive, this is the easiest way to ensure that session won't end up
- // in the inconsistent state:
- // - If device gets rebooted right before call to apexd, then apexd will never activate
- // apex files of this staged session. This will result in StagingManager failing
- // the session.
- // On the other hand, if the order of the calls was inverted (first call apexd, then
- // mark session as ready), then if a device gets rebooted right after the call to apexd,
- // only apex part of the train will be applied, leaving device in an inconsistent state.
- Slog.d(TAG, "Marking session " + session.sessionId() + " as ready");
- session.setSessionReady();
- if (session.isSessionReady()) {
- final boolean hasApex = session.containsApexSession();
- if (hasApex) {
- try {
- mApexManager.markStagedSessionReady(session.sessionId());
- notifyStagedApexObservers();
- } catch (PackageManagerException e) {
- session.setSessionFailed(e.error, e.getMessage());
- return;
- }
- }
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index c8deffb..d54acb7 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -21,9 +21,6 @@
"name": "CtsAppEnumerationTestCases"
},
{
- "name": "AppEnumerationInternalTests"
- },
- {
"name": "CtsMatchFlagTestCases"
},
{
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 61076ce..33970a2 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1357,7 +1357,7 @@
}
try {
ActivityManager.getService().startActivityFromRecents(targetTask.persistentId, null);
- } catch (RemoteException e) {
+ } catch (RemoteException | IllegalArgumentException e) {
Slog.e(TAG, "Failed to start task " + targetTask.persistentId + " from recents", e);
}
}
@@ -2937,6 +2937,24 @@
Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in"
+ " interceptKeyBeforeQueueing");
return key_consumed;
+ case KeyEvent.KEYCODE_VIDEO_APP_1:
+ case KeyEvent.KEYCODE_VIDEO_APP_2:
+ case KeyEvent.KEYCODE_VIDEO_APP_3:
+ case KeyEvent.KEYCODE_VIDEO_APP_4:
+ case KeyEvent.KEYCODE_VIDEO_APP_5:
+ case KeyEvent.KEYCODE_VIDEO_APP_6:
+ case KeyEvent.KEYCODE_VIDEO_APP_7:
+ case KeyEvent.KEYCODE_VIDEO_APP_8:
+ case KeyEvent.KEYCODE_FEATURED_APP_1:
+ case KeyEvent.KEYCODE_FEATURED_APP_2:
+ case KeyEvent.KEYCODE_FEATURED_APP_3:
+ case KeyEvent.KEYCODE_FEATURED_APP_4:
+ case KeyEvent.KEYCODE_DEMO_APP_1:
+ case KeyEvent.KEYCODE_DEMO_APP_2:
+ case KeyEvent.KEYCODE_DEMO_APP_3:
+ case KeyEvent.KEYCODE_DEMO_APP_4:
+ Slog.wtf(TAG, "KEYCODE_APP_X should be handled in interceptKeyBeforeQueueing");
+ return key_consumed;
case KeyEvent.KEYCODE_SYSRQ:
if (down && repeatCount == 0) {
mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
@@ -4040,6 +4058,26 @@
}
break;
}
+ case KeyEvent.KEYCODE_VIDEO_APP_1:
+ case KeyEvent.KEYCODE_VIDEO_APP_2:
+ case KeyEvent.KEYCODE_VIDEO_APP_3:
+ case KeyEvent.KEYCODE_VIDEO_APP_4:
+ case KeyEvent.KEYCODE_VIDEO_APP_5:
+ case KeyEvent.KEYCODE_VIDEO_APP_6:
+ case KeyEvent.KEYCODE_VIDEO_APP_7:
+ case KeyEvent.KEYCODE_VIDEO_APP_8:
+ case KeyEvent.KEYCODE_FEATURED_APP_1:
+ case KeyEvent.KEYCODE_FEATURED_APP_2:
+ case KeyEvent.KEYCODE_FEATURED_APP_3:
+ case KeyEvent.KEYCODE_FEATURED_APP_4:
+ case KeyEvent.KEYCODE_DEMO_APP_1:
+ case KeyEvent.KEYCODE_DEMO_APP_2:
+ case KeyEvent.KEYCODE_DEMO_APP_3:
+ case KeyEvent.KEYCODE_DEMO_APP_4: {
+ // Just drop if keys are not intercepted for direct key.
+ result &= ~ACTION_PASS_TO_USER;
+ break;
+ }
}
// Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
@@ -4878,6 +4916,12 @@
return mKeyguardDelegate.isInputRestricted();
}
+ /** {@inheritDoc} */
+ @Override
+ public boolean isKeyguardUnoccluding() {
+ return keyguardOn() && !mWindowManagerFuncs.isAppTransitionStateIdle();
+ }
+
@Override
public void dismissKeyguardLw(IKeyguardDismissCallback callback, CharSequence message) {
if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 81dd9c5..4895c0b 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -362,6 +362,12 @@
* as the top display.
*/
void moveDisplayToTop(int displayId);
+
+ /**
+ * Return whether the app transition state is idle.
+ * @return {@code true} if app transition state is idle on the default display.
+ */
+ boolean isAppTransitionStateIdle();
}
/**
@@ -962,6 +968,14 @@
public boolean isKeyguardOccluded();
/**
+ * Return whether the keyguard is unoccluding.
+ * @return {@code true} if the keyguard is unoccluding.
+ */
+ default boolean isKeyguardUnoccluding() {
+ return false;
+ }
+
+ /**
* @return true if in keyguard is on.
*/
boolean isKeyguardShowing();
diff --git a/services/core/java/com/android/server/security/OWNERS b/services/core/java/com/android/server/security/OWNERS
index e6f5826..41070f3 100644
--- a/services/core/java/com/android/server/security/OWNERS
+++ b/services/core/java/com/android/server/security/OWNERS
@@ -1,3 +1,4 @@
# Bug component: 36824
+per-file AttestationVerificationService.java = file:/core/java/android/security/attestationverification/OWNERS
per-file FileIntegrityService.java = victorhsieh@google.com
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 106cff1..0c78494 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -29,6 +29,7 @@
import static android.net.NetworkIdentity.OEM_PRIVATE;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.METERED_ALL;
+import static android.net.NetworkStats.METERED_YES;
import static android.net.NetworkStats.ROAMING_ALL;
import static android.net.NetworkTemplate.MATCH_ETHERNET;
import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD;
@@ -1348,7 +1349,7 @@
@Nullable private NetworkStats getUidNetworkStatsSnapshotForTransport(int transport) {
final NetworkTemplate template = (transport == TRANSPORT_CELLULAR)
? NetworkTemplate.buildTemplateMobileWithRatType(
- /*subscriptionId=*/null, NETWORK_TYPE_ALL)
+ /*subscriptionId=*/null, NETWORK_TYPE_ALL, METERED_YES)
: NetworkTemplate.buildTemplateWifiWildcard();
return getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
}
@@ -1388,7 +1389,8 @@
final List<NetworkStatsExt> ret = new ArrayList<>();
for (final int ratType : getAllCollapsedRatTypes()) {
final NetworkTemplate template =
- buildTemplateMobileWithRatType(subInfo.subscriberId, ratType);
+ buildTemplateMobileWithRatType(subInfo.subscriberId, ratType,
+ METERED_YES);
final NetworkStats stats =
getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
if (stats != null) {
@@ -1609,7 +1611,7 @@
int pullBluetoothBytesTransferLocked(int atomTag, List<StatsEvent> pulledData) {
BluetoothActivityEnergyInfo info = fetchBluetoothData();
- if (info == null || info.getUidTraffic() == null) {
+ if (info == null) {
return StatsManager.PULL_SKIP;
}
for (UidTraffic traffic : info.getUidTraffic()) {
@@ -2067,7 +2069,7 @@
if (info == null) {
return StatsManager.PULL_SKIP;
}
- pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, info.getTimeStamp(),
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, info.getTimestampMillis(),
info.getBluetoothStackState(), info.getControllerTxTimeMillis(),
info.getControllerRxTimeMillis(), info.getControllerIdleTimeMillis(),
info.getControllerEnergyUsed()));
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 00d01e1..a0f0367 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -97,6 +97,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
/**
* A note on locking: We rely on the fact that calls onto mBar are oneway or
@@ -142,6 +143,10 @@
@GuardedBy("mLock")
private IUdfpsHbmListener mUdfpsHbmListener;
+ @GuardedBy("mCurrentRequestAddTilePackages")
+ private final ArrayMap<String, Long> mCurrentRequestAddTilePackages = new ArrayMap<>();
+ private static final long REQUEST_TIME_OUT = TimeUnit.MINUTES.toNanos(5);
+
private class DeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
mBar.asBinder().unlinkToDeath(this,0);
@@ -1742,13 +1747,39 @@
return;
}
+ synchronized (mCurrentRequestAddTilePackages) {
+ Long lastTime = mCurrentRequestAddTilePackages.get(packageName);
+ final long currentTime = System.nanoTime();
+ if (lastTime != null && currentTime - lastTime < REQUEST_TIME_OUT) {
+ try {
+ callback.onTileRequest(
+ StatusBarManager.TILE_ADD_REQUEST_ERROR_REQUEST_IN_PROGRESS);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "requestAddTile", e);
+ }
+ return;
+ } else {
+ if (lastTime != null) {
+ cancelRequestAddTileInternal(packageName);
+ }
+ }
+
+ mCurrentRequestAddTilePackages.put(packageName, currentTime);
+ }
+
IAddTileResultCallback proxyCallback = new IAddTileResultCallback.Stub() {
@Override
- public void onTileRequest(int i) throws RemoteException {
+ public void onTileRequest(int i) {
if (i == StatusBarManager.TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED) {
i = StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED;
}
- callback.onTileRequest(i);
+ if (clearTileAddRequest(packageName)) {
+ try {
+ callback.onTileRequest(i);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "requestAddTile - callback", e);
+ }
+ }
}
};
@@ -1757,11 +1788,12 @@
if (mBar != null) {
try {
mBar.requestAddTile(componentName, appName, label, icon, proxyCallback);
+ return;
} catch (RemoteException e) {
Slog.e(TAG, "requestAddTile", e);
}
- return;
}
+ clearTileAddRequest(packageName);
try {
callback.onTileRequest(StatusBarManager.TILE_ADD_REQUEST_ERROR_NO_STATUS_BAR_SERVICE);
} catch (RemoteException e) {
@@ -1769,6 +1801,29 @@
}
}
+ @Override
+ public void cancelRequestAddTile(@NonNull String packageName) {
+ enforceStatusBar();
+ cancelRequestAddTileInternal(packageName);
+ }
+
+ private void cancelRequestAddTileInternal(String packageName) {
+ clearTileAddRequest(packageName);
+ if (mBar != null) {
+ try {
+ mBar.cancelRequestAddTile(packageName);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "requestAddTile", e);
+ }
+ }
+ }
+
+ private boolean clearTileAddRequest(String packageName) {
+ synchronized (mCurrentRequestAddTilePackages) {
+ return mCurrentRequestAddTilePackages.remove(packageName) != null;
+ }
+ }
+
public String[] getStatusBarIcons() {
return mContext.getResources().getStringArray(R.array.config_statusBarIcons);
}
@@ -1898,6 +1953,16 @@
}
pw.println();
}
+ ArrayList<String> requests;
+ synchronized (mCurrentRequestAddTilePackages) {
+ requests = new ArrayList<>(mCurrentRequestAddTilePackages.keySet());
+ }
+ pw.println(" mCurrentRequestAddTilePackages=[");
+ final int reqN = requests.size();
+ for (int i = 0; i < reqN; i++) {
+ pw.println(" " + requests.get(i) + ",");
+ }
+ pw.println(" ]");
}
}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 0f37450..e98fa28 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -86,6 +86,9 @@
private static final String LOG_TAG = "TextClassificationManagerService";
+ // TODO: consider using device config to control it.
+ private static final boolean DEBUG = false;
+
private static final ITextClassifierCallback NO_OP_CALLBACK = new ITextClassifierCallback() {
@Override
public void onSuccess(Bundle result) {}
@@ -175,8 +178,6 @@
private final String mDefaultTextClassifierPackage;
@Nullable
private final String mSystemTextClassifierPackage;
- // TODO: consider using device config to control it.
- private boolean DEBUG = false;
private TextClassificationManagerService(Context context) {
mContext = Objects.requireNonNull(context);
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 92e0845..f57a852 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -1075,17 +1075,22 @@
}
if (shouldRecreateAudioPatch) {
mCommittedVolume = volume;
- if (mAudioPatch != null) {
- mAudioManager.releaseAudioPatch(mAudioPatch);
- }
- mAudioManager.createAudioPatch(
+ // only recreate if something was updated or audioPath is null
+ if (mAudioPatch == null || sinkUpdated ||sourceUpdated ) {
+ if (mAudioPatch != null) {
+ mAudioManager.releaseAudioPatch(mAudioPatch);
+ audioPatchArray[0] = null;
+ }
+ mAudioManager.createAudioPatch(
audioPatchArray,
new AudioPortConfig[] { sourceConfig },
sinkConfigs.toArray(new AudioPortConfig[sinkConfigs.size()]));
- mAudioPatch = audioPatchArray[0];
- if (sourceGainConfig != null) {
- mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
+ mAudioPatch = audioPatchArray[0];
}
+ }
+
+ if (sourceGainConfig != null) {
+ mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 44767f3..9222b6d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -86,8 +86,6 @@
import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_METADATA;
import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_OVERRIDE;
import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
-import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
-import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
import static android.content.res.Configuration.ASSETS_SEQ_UNDEFINED;
import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -1141,10 +1139,11 @@
if (info.getMaxAspectRatio() != 0) {
pw.println(prefix + "maxAspectRatio=" + info.getMaxAspectRatio());
}
- if (info.getMinAspectRatio() != 0) {
- pw.println(prefix + "minAspectRatio=" + info.getMinAspectRatio());
+ final float minAspectRatio = getMinAspectRatio();
+ if (minAspectRatio != 0) {
+ pw.println(prefix + "minAspectRatio=" + minAspectRatio);
}
- if (info.getMinAspectRatio() != info.getManifestMinAspectRatio()) {
+ if (minAspectRatio != info.getManifestMinAspectRatio()) {
// Log the fact that we've overridden the min aspect ratio from the manifest
pw.println(prefix + "manifestMinAspectRatio="
+ info.getManifestMinAspectRatio());
@@ -1443,13 +1442,7 @@
}
final Task rootTask = getRootTask();
- // If we reparent, make sure to remove ourselves from the old animation registry.
- if (mAnimatingActivityRegistry != null) {
- mAnimatingActivityRegistry.notifyFinished(this);
- }
- mAnimatingActivityRegistry = rootTask != null
- ? rootTask.getAnimatingActivityRegistry()
- : null;
+ updateAnimatingActivityRegistry();
if (task == mLastParentBeforePip) {
// Activity's reparented back from pip, clear the links once established
@@ -1482,6 +1475,20 @@
}
}
+ void updateAnimatingActivityRegistry() {
+ final Task rootTask = getRootTask();
+ final AnimatingActivityRegistry registry = rootTask != null
+ ? rootTask.getAnimatingActivityRegistry()
+ : null;
+
+ // If we reparent, make sure to remove ourselves from the old animation registry.
+ if (mAnimatingActivityRegistry != null && mAnimatingActivityRegistry != registry) {
+ mAnimatingActivityRegistry.notifyFinished(this);
+ }
+
+ mAnimatingActivityRegistry = registry;
+ }
+
/**
* Sets {@link #mLastParentBeforePip} to the current parent Task, it's caller's job to ensure
* {@link #getTask()} is set before this is called.
@@ -4108,19 +4115,40 @@
* conditions a) above.
* Multi-windowing mode will be exited if {@code true} is returned.
*/
- boolean canShowWhenLocked() {
- if (!inPinnedWindowingMode() && (mShowWhenLocked || containsShowWhenLockedWindow())) {
+ private static boolean canShowWhenLocked(ActivityRecord r) {
+ if (r == null || r.getTaskFragment() == null) {
+ return false;
+ }
+ if (!r.inPinnedWindowingMode() && (r.mShowWhenLocked || r.containsShowWhenLockedWindow())) {
return true;
- } else if (mInheritShownWhenLocked) {
- final ActivityRecord r = task.getActivityBelow(this);
- return r != null && !r.inPinnedWindowingMode() && (r.mShowWhenLocked
- || r.containsShowWhenLockedWindow());
+ } else if (r.mInheritShownWhenLocked) {
+ final ActivityRecord activity = r.getTaskFragment().getActivityBelow(r);
+ return activity != null && !activity.inPinnedWindowingMode()
+ && (activity.mShowWhenLocked || activity.containsShowWhenLockedWindow());
} else {
return false;
}
}
/**
+ * Determines if the activity can show while lock-screen is displayed. System displays
+ * activities while lock-screen is displayed only if all activities
+ * {@link #canShowWhenLocked(ActivityRecord)}.
+ * @see #canShowWhenLocked(ActivityRecord)
+ */
+ boolean canShowWhenLocked() {
+ final TaskFragment taskFragment = getTaskFragment();
+ if (taskFragment != null && taskFragment.getAdjacentTaskFragment() != null
+ && taskFragment.isEmbedded()) {
+ final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
+ final ActivityRecord r = adjacentTaskFragment.getTopNonFinishingActivity();
+ return canShowWhenLocked(this) && canShowWhenLocked(r);
+ } else {
+ return canShowWhenLocked(this);
+ }
+ }
+
+ /**
* @return Whether we are allowed to show non-starting windows at the moment. We disallow
* showing windows during transitions in case we have windows that have wide-color-gamut
* color mode set to avoid jank in the middle of the transition.
@@ -4138,29 +4166,6 @@
true /* topToBottom */);
}
- WindowState getImeTargetBelowWindow(WindowState w) {
- final int index = mChildren.indexOf(w);
- if (index > 0) {
- return mChildren.get(index - 1)
- .getWindow(WindowState::canBeImeTarget);
- }
- return null;
- }
-
- WindowState getHighestAnimLayerWindow(WindowState currentTarget) {
- WindowState candidate = null;
- for (int i = mChildren.indexOf(currentTarget); i >= 0; i--) {
- final WindowState w = mChildren.get(i);
- if (w.mRemoved) {
- continue;
- }
- if (candidate == null) {
- candidate = w;
- }
- }
- return candidate;
- }
-
@Override
boolean forAllActivities(Predicate<ActivityRecord> callback, boolean traverseTopToBottom) {
return callback.test(this);
@@ -6995,11 +7000,6 @@
clearThumbnail();
}
- @VisibleForTesting
- WindowContainerThumbnail getThumbnail() {
- return mThumbnail;
- }
-
private void clearThumbnail() {
if (mThumbnail == null) {
return;
@@ -7012,9 +7012,6 @@
return mTransit;
}
- int getTransitFlags() {
- return mTransitFlags;
- }
void registerRemoteAnimations(RemoteAnimationDefinition definition) {
mRemoteAnimationDefinition = definition;
@@ -7209,7 +7206,7 @@
return false;
}
}
- return !isResizeable() && (info.isFixedOrientation() || info.hasFixedAspectRatio())
+ return !isResizeable() && (info.isFixedOrientation() || hasFixedAspectRatio())
// The configuration of non-standard type should be enforced by system.
// {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} is set when this activity is
// added to a task, but this function is called when resolving the launch params, at
@@ -7880,13 +7877,14 @@
return false;
}
}
- if (info.getMinAspectRatio() > 0) {
+ final float minAspectRatio = getMinAspectRatio();
+ if (minAspectRatio > 0) {
// The activity should have at least the min aspect ratio, so this checks if the
// container still has available space to provide larger aspect ratio.
final float containerAspectRatio =
(0.5f + Math.max(containerAppWidth, containerAppHeight))
/ Math.min(containerAppWidth, containerAppHeight);
- if (containerAspectRatio <= info.getMinAspectRatio()) {
+ if (containerAspectRatio <= minAspectRatio) {
// The long side has reached the parent.
return false;
}
@@ -8066,20 +8064,6 @@
super.onResize();
}
- /** Returns true if the configuration is compatible with this activity. */
- boolean isConfigurationCompatible(Configuration config) {
- final int orientation = getRequestedOrientation();
- if (isFixedOrientationPortrait(orientation)
- && config.orientation != ORIENTATION_PORTRAIT) {
- return false;
- }
- if (isFixedOrientationLandscape(orientation)
- && config.orientation != ORIENTATION_LANDSCAPE) {
- return false;
- }
- return true;
- }
-
private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
Rect containingBounds) {
return applyAspectRatio(outBounds, containingAppBounds, containingBounds,
@@ -8097,8 +8081,7 @@
Rect containingBounds, float desiredAspectRatio, boolean fixedOrientationLetterboxed) {
final float maxAspectRatio = info.getMaxAspectRatio();
final Task rootTask = getRootTask();
- final float minAspectRatio = info.getMinAspectRatio();
-
+ final float minAspectRatio = getMinAspectRatio();
if (task == null || rootTask == null
|| (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()
&& !fixedOrientationLetterboxed)
@@ -8202,6 +8185,20 @@
}
/**
+ * Returns the min aspect ratio of this activity.
+ */
+ private float getMinAspectRatio() {
+ return info.getMinAspectRatio(getRequestedOrientation());
+ }
+
+ /**
+ * Returns true if the activity has maximum or minimum aspect ratio.
+ */
+ private boolean hasFixedAspectRatio() {
+ return info.hasFixedAspectRatio(getRequestedOrientation());
+ }
+
+ /**
* Returns the aspect ratio of the given {@code rect}.
*/
static float computeAspectRatio(Rect rect) {
@@ -8930,7 +8927,7 @@
}
proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled());
proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
- proto.write(MIN_ASPECT_RATIO, info.getMinAspectRatio());
+ proto.write(MIN_ASPECT_RATIO, getMinAspectRatio());
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a1a357e..690397f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -221,6 +221,7 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationRunner;
+import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
@@ -455,6 +456,10 @@
VrController mVrController;
KeyguardController mKeyguardController;
private final ClientLifecycleManager mLifecycleManager;
+
+ @Nullable
+ private final BackGestureController mBackGestureController;
+
private TaskChangeNotificationController mTaskChangeNotificationController;
/** The controller for all operations related to locktask. */
private LockTaskController mLockTaskController;
@@ -496,9 +501,6 @@
*/
private Configuration mTempConfig = new Configuration();
- /** Temporary to avoid allocations. */
- final StringBuilder mStringBuilder = new StringBuilder(256);
-
/**
* Whether normal application switches are allowed; a call to {@link #stopAppSwitches()
* disables this.
@@ -814,6 +816,8 @@
mSystemThread = ActivityThread.currentActivityThread();
mUiContext = mSystemThread.getSystemUiContext();
mLifecycleManager = new ClientLifecycleManager();
+ mBackGestureController = BackGestureController.isEnabled() ? new BackGestureController()
+ : null;
mVisibleActivityProcessTracker = new VisibleActivityProcessTracker(this);
mInternal = new LocalService();
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
@@ -1747,6 +1751,14 @@
}
}
+ @Override
+ public void startBackPreview(IRemoteAnimationRunner runner) {
+ if (mBackGestureController == null) {
+ return;
+ }
+ mBackGestureController.startBackPreview();
+ }
+
/**
* Public API to check if the client is allowed to start an activity on specified display.
*
@@ -3081,11 +3093,6 @@
throw new SecurityException(msg);
}
- @VisibleForTesting
- int checkGetTasksPermission(String permission, int pid, int uid) {
- return checkPermission(permission, pid, uid);
- }
-
static int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
@@ -3105,10 +3112,10 @@
return true;
}
- boolean allowed = checkGetTasksPermission(android.Manifest.permission.REAL_GET_TASKS,
+ boolean allowed = checkPermission(android.Manifest.permission.REAL_GET_TASKS,
callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
if (!allowed) {
- if (checkGetTasksPermission(android.Manifest.permission.GET_TASKS,
+ if (checkPermission(android.Manifest.permission.GET_TASKS,
callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) {
// Temporary compatibility: some existing apps on the system image may
// still be requesting the old permission and not switched to the new
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 7817f54..03d6590 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -292,6 +292,10 @@
setAppTransitionState(APP_STATE_IDLE);
}
+ boolean isIdle() {
+ return mAppTransitionState == APP_STATE_IDLE;
+ }
+
boolean isTimeout() {
return mAppTransitionState == APP_STATE_TIMEOUT;
}
diff --git a/services/core/java/com/android/server/wm/BackGestureController.java b/services/core/java/com/android/server/wm/BackGestureController.java
new file mode 100644
index 0000000..f8f6254
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BackGestureController.java
@@ -0,0 +1,37 @@
+/*
+ * 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.wm;
+
+import android.os.SystemProperties;
+
+/**
+ * Controller to handle actions related to the back gesture on the server side.
+ */
+public class BackGestureController {
+
+ private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+
+ public static boolean isEnabled() {
+ return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
+ }
+
+ /**
+ * Start a remote animation the back gesture.
+ */
+ public void startBackPreview() {
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index cfe1aef..806d27a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -114,10 +114,12 @@
import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_INPUT_TARGET;
import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_TARGET;
import static com.android.server.wm.DisplayContentProto.INSETS_SOURCE_PROVIDERS;
+import static com.android.server.wm.DisplayContentProto.IS_SLEEPING;
import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
+import static com.android.server.wm.DisplayContentProto.SLEEP_TOKENS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
@@ -467,9 +469,6 @@
@VisibleForTesting
boolean isDefaultDisplay;
- /** Window tokens that are in the process of exiting, but still on screen for animations. */
- final ArrayList<WindowToken> mExitingTokens = new ArrayList<>();
-
/** Detect user tapping outside of current focused task bounds .*/
@VisibleForTesting
final TaskTapPointerEventListener mTapDetector;
@@ -556,9 +555,6 @@
/** Windows removed since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
final ArrayList<WindowState> mWinRemovedSinceNullFocus = new ArrayList<>();
- /** Windows whose client's insets states are not up-to-date. */
- final ArrayList<WindowState> mWinInsetsChanged = new ArrayList<>();
-
private ScreenRotationAnimation mScreenRotationAnimation;
/**
@@ -638,9 +634,6 @@
/** The surface parent of the IME container. */
private SurfaceControl mInputMethodSurfaceParent;
- /** If {@code true} hold off on modifying the animation layer of {@link #mImeLayeringTarget} */
- boolean mImeLayeringTargetWaitingAnim;
-
/** The screenshot IME surface to place on the task while transitioning to the next task. */
SurfaceControl mImeScreenshot;
@@ -805,13 +798,16 @@
};
private final Consumer<WindowState> mPerformLayout = w -> {
+ if (w.mLayoutAttached || w.skipLayout()) {
+ return;
+ }
+
// Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
// wasting time and funky changes while a window is animating away.
final boolean gone = w.isGoneForLayout();
- if (DEBUG_LAYOUT && !w.mLayoutAttached) {
+ if (DEBUG_LAYOUT) {
Slog.v(TAG, "1ST PASS " + w + ": gone=" + gone + " mHaveFrame=" + w.mHaveFrame
- + " mLayoutAttached=" + w.mLayoutAttached
+ " config reported=" + w.isLastConfigReportedToClient());
final ActivityRecord activity = w.mActivityRecord;
if (gone) Slog.v(TAG, " GONE: mViewVisibility=" + w.mViewVisibility
@@ -827,7 +823,7 @@
// If this view is GONE, then skip it -- keep the current frame, and let the caller know
// so they can ignore it if they want. (We do the normal layout for INVISIBLE windows,
// since that means "perform layout as normal, just don't display").
- if ((!gone || !w.mHaveFrame || w.mLayoutNeeded) && !w.mLayoutAttached) {
+ if (!gone || !w.mHaveFrame || w.mLayoutNeeded) {
if (mTmpInitial) {
w.resetContentChanged();
}
@@ -854,34 +850,34 @@
}
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrame()
- + " mContainingFrame=" + w.getContainingFrame()
+ + " mParentFrame=" + w.getParentFrame()
+ " mDisplayFrame=" + w.getDisplayFrame());
}
};
private final Consumer<WindowState> mPerformLayoutAttached = w -> {
- if (w.mLayoutAttached) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + w + " mHaveFrame=" + w.mHaveFrame
- + " mViewVisibility=" + w.mViewVisibility
- + " mRelayoutCalled=" + w.mRelayoutCalled);
- // If this view is GONE, then skip it -- keep the current frame, and let the caller
- // know so they can ignore it if they want. (We do the normal layout for INVISIBLE
- // windows, since that means "perform layout as normal, just don't display").
- if ((w.mViewVisibility != GONE && w.mRelayoutCalled) || !w.mHaveFrame
- || w.mLayoutNeeded) {
- if (mTmpInitial) {
- //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
- w.resetContentChanged();
- }
- w.mSurfacePlacementNeeded = true;
- w.mLayoutNeeded = false;
- w.prelayout();
- getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
- w.mLayoutSeq = mLayoutSeq;
- if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrame()
- + " mContainingFrame=" + w.getContainingFrame()
- + " mDisplayFrame=" + w.getDisplayFrame());
+ if (!w.mLayoutAttached || w.skipLayout()) {
+ return;
+ }
+ if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + w + " mHaveFrame=" + w.mHaveFrame
+ + " mViewVisibility=" + w.mViewVisibility
+ + " mRelayoutCalled=" + w.mRelayoutCalled);
+ // If this view is GONE, then skip it -- keep the current frame, and let the caller
+ // know so they can ignore it if they want. (We do the normal layout for INVISIBLE
+ // windows, since that means "perform layout as normal, just don't display").
+ if ((w.mViewVisibility != GONE && w.mRelayoutCalled) || !w.mHaveFrame
+ || w.mLayoutNeeded) {
+ if (mTmpInitial) {
+ w.resetContentChanged();
}
+ w.mSurfacePlacementNeeded = true;
+ w.mLayoutNeeded = false;
+ w.prelayout();
+ getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
+ w.mLayoutSeq = mLayoutSeq;
+ if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrame()
+ + " mParentFrame=" + w.getParentFrame()
+ + " mDisplayFrame=" + w.getDisplayFrame());
}
};
@@ -3264,6 +3260,11 @@
proto.write(FOCUSED_ROOT_TASK_ID, INVALID_TASK_ID);
}
proto.write(DISPLAY_READY, isReady());
+ proto.write(IS_SLEEPING, isSleeping());
+ for (int i = 0; i < mAllSleepTokens.size(); ++i) {
+ mAllSleepTokens.get(i).writeTagToProto(proto, SLEEP_TOKENS);
+ }
+
if (mImeLayeringTarget != null) {
mImeLayeringTarget.dumpDebug(proto, INPUT_METHOD_TARGET, logLevel);
}
@@ -3359,17 +3360,6 @@
});
pw.println();
- if (!mExitingTokens.isEmpty()) {
- pw.println();
- pw.println(" Exiting tokens:");
- for (int i = mExitingTokens.size() - 1; i >= 0; i--) {
- final WindowToken token = mExitingTokens.get(i);
- pw.print(" Exiting #"); pw.print(i);
- pw.print(' '); pw.print(token);
- pw.println(':');
- token.dump(pw, " ", dumpAll);
- }
- }
final ScreenRotationAnimation rotationAnimation = getRotationAnimation();
if (rotationAnimation != null) {
@@ -4115,6 +4105,8 @@
target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
}
setImeInputTarget(target);
+ mInsetsStateController.updateAboveInsetsState(mInputMethodWindow, mInsetsStateController
+ .getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
updateImeControlTarget();
}
}
@@ -4621,21 +4613,6 @@
return ret;
}
- void setExitingTokensHasVisible(boolean hasVisible) {
- for (int i = mExitingTokens.size() - 1; i >= 0; i--) {
- mExitingTokens.get(i).hasVisible = hasVisible;
- }
- }
-
- void removeExistingTokensIfPossible() {
- for (int i = mExitingTokens.size() - 1; i >= 0; i--) {
- final WindowToken token = mExitingTokens.get(i);
- if (!token.hasVisible) {
- mExitingTokens.remove(i);
- }
- }
- }
-
@Override
void onDescendantOverrideConfigurationChanged() {
setLayoutNeeded();
@@ -4993,12 +4970,6 @@
}
}
- void assignRootTaskOrdering() {
- forAllTaskDisplayAreas(taskDisplayArea -> {
- taskDisplayArea.assignRootTaskOrdering(getPendingTransaction());
- });
- }
-
/**
* Increment the deferral count to determine whether to update the IME target.
*/
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 253b3a5..180ee61 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -43,12 +43,12 @@
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
+import static android.view.WindowLayout.UNSPECIFIED_LENGTH;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
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_LAYOUT_NO_LIMITS;
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;
@@ -65,7 +65,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
@@ -115,6 +114,7 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
+import android.gui.DropInputMode;
import android.hardware.power.Boost;
import android.os.Handler;
import android.os.IBinder;
@@ -164,7 +164,6 @@
import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
-import com.android.server.wm.InputMonitor.EventReceiverInputConsumer;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -395,7 +394,6 @@
// -------- PolicyHandler --------
private static final int MSG_REQUEST_TRANSIENT_BARS = 2;
- private static final int MSG_DISPOSE_INPUT_CONSUMER = 3;
private static final int MSG_ENABLE_POINTER_LOCATION = 4;
private static final int MSG_DISABLE_POINTER_LOCATION = 5;
@@ -424,9 +422,6 @@
}
}
break;
- case MSG_DISPOSE_INPUT_CONSUMER:
- disposeInputConsumer((EventReceiverInputConsumer) msg.obj);
- break;
case MSG_ENABLE_POINTER_LOCATION:
enablePointerLocation();
break;
@@ -962,6 +957,20 @@
}
/**
+ * Add additional policy if needed to ensure the window or its children should not receive any
+ * input.
+ */
+ public void setDropInputModePolicy(WindowState win, LayoutParams attrs) {
+ if (attrs.type == TYPE_TOAST
+ && (attrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) == 0) {
+ // Toasts should not receive input. These windows should not have any children, so
+ // force this hierarchy of windows to drop all input.
+ mService.mTransactionFactory.get()
+ .setDropInputMode(win.getSurfaceControl(), DropInputMode.ALL).apply();
+ }
+ }
+
+ /**
* Check if a window can be added to the system.
*
* Currently enforces that two window types are singletons per display:
@@ -1704,10 +1713,6 @@
layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR));
return;
}
- if (win.mActivityRecord != null && win.mActivityRecord.mWaitForEnteringPinnedMode) {
- // Skip layout of the window when in transition to pip mode.
- return;
- }
final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);
final int type = attrs.type;
@@ -1719,37 +1724,50 @@
final Rect pf = windowFrames.mParentFrame;
final Rect df = windowFrames.mDisplayFrame;
+ final Rect f = windowFrames.mFrame;
final Rect attachedWindowFrame = attached != null ? attached.getFrame() : null;
sTmpLastParentFrame.set(pf);
- // Override the bounds in window token has many side effects. Directly use the display
- // frame set for the simulated layout for this case.
- final Rect winBounds = windowFrames.mIsSimulatingDecorWindow ? df : win.getBounds();
+ final Rect winBounds;
+ final int requestedWidth;
+ final int requestedHeight;
+ if (windowFrames.mIsSimulatingDecorWindow) {
+ // Override the bounds in window token has many side effects. Directly use the display
+ // frame set for the simulated layout.
+ winBounds = df;
+
+ // The view hierarchy has not been measured in the simulated layout. Use
+ // UNSPECIFIED_LENGTH as the requested width and height so that WindowLayout will choose
+ // the proper values in this case.
+ requestedWidth = UNSPECIFIED_LENGTH;
+ requestedHeight = UNSPECIFIED_LENGTH;
+ } else {
+ winBounds = win.getBounds();
+ requestedWidth = win.mRequestedWidth;
+ requestedHeight = win.mRequestedHeight;
+ }
final boolean clippedByDisplayCutout = mWindowLayout.computeWindowFrames(attrs,
- win.getInsetsState(), displayFrames.mDisplayCutoutSafe, winBounds,
- win.getRequestedVisibilities(), attachedWindowFrame, df, pf);
+ win.getInsetsState(), displayFrames.mDisplayCutoutSafe,
+ winBounds, win.getWindowingMode(), requestedWidth, requestedHeight,
+ win.getRequestedVisibilities(), attachedWindowFrame, win.mGlobalScale,
+ df, pf, f);
windowFrames.setParentFrameWasClippedByDisplayCutout(clippedByDisplayCutout);
- // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
- // Also, we don't allow windows in multi-window mode to extend out of the screen.
- if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR
- && !win.inMultiWindowMode()) {
- df.left = df.top = -10000;
- df.right = df.bottom = 10000;
- }
-
if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
+ ": sim=#" + Integer.toHexString(sim)
+ " attach=" + attached + " type=" + type
- + String.format(" flags=0x%08x", fl)
- + " pf=" + pf.toShortString() + " df=" + df.toShortString());
+ + " flags=" + ViewDebug.flagsToString(LayoutParams.class, "flags", fl)
+ + " pf=" + pf.toShortString() + " df=" + df.toShortString()
+ + " f=" + f.toShortString());
if (!sTmpLastParentFrame.equals(pf)) {
windowFrames.setContentChanged(true);
}
- win.computeFrameAndUpdateSourceFrame(displayFrames);
+ if (!windowFrames.mIsSimulatingDecorWindow) {
+ win.setFrame();
+ }
}
WindowState getTopFullscreenOpaqueWindow() {
@@ -2550,12 +2568,6 @@
mImmersiveModeConfirmation.confirmCurrentPrompt();
}
- private void disposeInputConsumer(EventReceiverInputConsumer inputConsumer) {
- if (inputConsumer != null) {
- inputConsumer.dispose();
- }
- }
-
boolean isKeyguardShowing() {
return mService.mPolicy.isKeyguardShowing();
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index fa4d27c..d17c109 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -54,14 +54,12 @@
import android.graphics.Region;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Slog;
import android.view.InputChannel;
-import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
import android.view.SurfaceControl;
@@ -112,44 +110,6 @@
private WeakReference<ActivityRecord> mActiveRecentsActivity = null;
private WeakReference<ActivityRecord> mActiveRecentsLayerRef = null;
- /**
- * Representation of a input consumer that the policy has added to the window manager to consume
- * input events going to windows below it.
- */
- static final class EventReceiverInputConsumer extends InputConsumerImpl {
- private InputMonitor mInputMonitor;
- private final InputEventReceiver mInputEventReceiver;
-
- EventReceiverInputConsumer(WindowManagerService service, InputMonitor monitor,
- Looper looper, String name,
- InputEventReceiver.Factory inputEventReceiverFactory,
- int clientPid, UserHandle clientUser, int displayId) {
- super(service, null /* token */, name, null /* inputChannel */, clientPid, clientUser,
- displayId);
- mInputMonitor = monitor;
- mInputEventReceiver = inputEventReceiverFactory.createInputEventReceiver(
- mClientChannel, looper);
- }
-
- /** Removes the input consumer from the window manager. */
- void dismiss() {
- synchronized (mService.mGlobalLock) {
- mInputMonitor.mInputConsumers.remove(mName);
- hide(mInputMonitor.mInputTransaction);
- mInputMonitor.updateInputWindowsLw(true /* force */);
- }
- }
-
- /** Disposes the input consumer and input receiver from the associated thread. */
- void dispose() {
- synchronized (mService.mGlobalLock) {
- disposeChannelsLw(mInputMonitor.mInputTransaction);
- mInputEventReceiver.dispose();
- mInputMonitor.updateInputWindowsLw(true /* force */);
- }
- }
- }
-
private class UpdateInputWindows implements Runnable {
@Override
public void run() {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index b46b2f7..3f573fa 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -291,19 +291,10 @@
for (int i = mProviders.size() - 1; i >= 0; i--) {
mProviders.valueAt(i).onPostLayout();
}
- final ArrayList<WindowState> winInsetsChanged = mDisplayContent.mWinInsetsChanged;
if (!mLastState.equals(mState)) {
mLastState.set(mState, true /* copySources */);
notifyInsetsChanged();
- } else {
- // The global insets state has not changed but there might be windows whose conditions
- // (e.g., z-order) have changed. They can affect the insets states that we dispatch to
- // the clients.
- for (int i = winInsetsChanged.size() - 1; i >= 0; i--) {
- mDispatchInsetsChanged.accept(winInsetsChanged.get(i));
- }
}
- winInsetsChanged.clear();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 1277e35..e10ae37 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -839,12 +839,6 @@
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
- // Initialize state of exiting tokens.
- for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
- final DisplayContent displayContent = mChildren.get(displayNdx);
- displayContent.setExitingTokensHasVisible(false);
- }
-
mHoldScreen = null;
mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mUserActivityTimeout = -1;
@@ -951,12 +945,6 @@
mWmService.mDestroySurface.clear();
}
- // Time to remove any exiting tokens?
- for (int displayNdx = mChildren.size() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent displayContent = mChildren.get(displayNdx);
- displayContent.removeExistingTokensIfPossible();
- }
-
for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.pendingLayoutChanges != 0) {
@@ -2651,6 +2639,7 @@
token = new SleepToken(tag, displayId);
mSleepTokens.put(tokenKey, token);
display.mAllSleepTokens.add(token);
+ ProtoLog.d(WM_DEBUG_STATES, "Create sleep token: tag=%s, displayId=%d", tag, displayId);
} else {
throw new RuntimeException("Create the same sleep token twice: " + token);
}
@@ -2669,6 +2658,8 @@
return;
}
+ ProtoLog.d(WM_DEBUG_STATES, "Remove sleep token: tag=%s, displayId=%d", token.mTag,
+ token.mDisplayId);
display.mAllSleepTokens.remove(token);
if (display.mAllSleepTokens.isEmpty()) {
mService.updateSleepIfNeededLocked();
@@ -3676,6 +3667,10 @@
return "{\"" + mTag + "\", display " + mDisplayId
+ ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
}
+
+ void writeTagToProto(ProtoOutputStream proto, long fieldId) {
+ proto.write(fieldId, mTag);
+ }
}
private class RankTaskLayersRunnable implements Runnable {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 6b7f92a..b8b9bcc 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.Manifest.permission.DEVICE_POWER;
import static android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.Manifest.permission.HIDE_OVERLAY_WINDOWS;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
@@ -107,7 +106,6 @@
final boolean mCanCreateSystemApplicationOverlay;
final boolean mCanHideNonSystemOverlayWindows;
- final boolean mCanAcquireSleepToken;
private AlertWindowNotification mAlertWindowNotification;
private boolean mShowingAlertWindowNotificationAllowed;
private boolean mClientDead = false;
@@ -134,8 +132,6 @@
== PERMISSION_GRANTED;
mCanStartTasksFromRecents = service.mContext.checkCallingOrSelfPermission(
START_TASKS_FROM_RECENTS) == PERMISSION_GRANTED;
- mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
- == PERMISSION_GRANTED;
mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
mDragDropController = mService.mDragDropController;
StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6987cdb..9767c8b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -471,12 +471,6 @@
// to layout without loading all the task snapshots
final PersistedTaskSnapshotData mLastTaskSnapshotData;
- // If set to true, the task will report that it is not in the floating
- // state regardless of it's root task affiliation. As the floating state drives
- // production of content insets this can be used to preserve them across
- // root task moves and we in fact do so when moving from full screen to pinned.
- private boolean mPreserveNonFloatingState = false;
-
private Dimmer mDimmer = new Dimmer(this);
private final Rect mTmpDimBoundsRect = new Rect();
@@ -1224,6 +1218,9 @@
adjustBoundsForDisplayChangeIfNeeded(getDisplayContent());
mRootWindowContainer.updateUIDsPresentOnDisplay();
+
+ // Ensure all animations are finished at same time in split-screen mode.
+ forAllActivities(ActivityRecord::updateAnimatingActivityRegistry);
}
@Override
@@ -2582,11 +2579,6 @@
reparent(rootTask, position);
rootTask.positionChildAt(position, this, moveParents);
-
- // If we are moving from the fullscreen root task to the root pinned task then we want to
- // preserve our insets so that there will not be a jump in the area covered by system
- // decorations. We rely on the pinned animation to later unset this value.
- mPreserveNonFloatingState = rootTask.inPinnedWindowingMode();
}
public int setBounds(Rect bounds, boolean forceResize) {
@@ -2899,16 +2891,6 @@
mForceShowForAllUsers = forceShowForAllUsers;
}
- /**
- * When we are in a floating root task (Freeform, Pinned, ...) we calculate
- * insets differently. However if we are animating to the fullscreen root task
- * we need to begin calculating insets as if we were fullscreen, otherwise
- * we will have a jump at the end.
- */
- boolean isFloating() {
- return getWindowConfiguration().tasksAreFloating() && !mPreserveNonFloatingState;
- }
-
/** Returns the top-most activity that occludes the given one, or {@code null} if none. */
@Nullable
ActivityRecord getOccludingActivityAbove(ActivityRecord activity) {
@@ -3246,10 +3228,6 @@
return "Task=" + mTaskId;
}
- void clearPreserveNonFloatingState() {
- mPreserveNonFloatingState = false;
- }
-
@Override
Dimmer getDimmer() {
// If the window is in multi-window mode, we want to dim at the Task level to ensure the dim
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 7e784ae..901f14f 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -798,12 +798,9 @@
int layer = 0;
// Place root home tasks to the bottom.
layer = adjustRootTaskLayer(t, mTmpHomeChildren, layer);
- adjustRootTaskLayer(t, mTmpNormalChildren, layer);
-
- // Always on top tasks layer should higher than split divider layer so set it as start.
- t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER);
- layer = SPLIT_DIVIDER_LAYER + 1;
+ layer = adjustRootTaskLayer(t, mTmpNormalChildren, layer);
adjustRootTaskLayer(t, mTmpAlwaysOnTopChildren, layer);
+ t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER);
}
/**
@@ -818,19 +815,33 @@
ArrayList<WindowContainer> children, int startLayer) {
mTmpNeedsZBoostIndexes.clear();
final int childCount = children.size();
+ boolean hasAdjacentTask = false;
for (int i = 0; i < childCount; i++) {
final WindowContainer child = children.get(i);
final TaskDisplayArea childTda = child.asTaskDisplayArea();
-
- boolean childNeedsZBoost = childTda != null
+ final boolean childNeedsZBoost = childTda != null
? childTda.childrenNeedZBoost()
: child.needsZBoost();
- if (!childNeedsZBoost) {
- child.assignLayer(t, startLayer++);
- } else {
+ if (childNeedsZBoost) {
mTmpNeedsZBoostIndexes.add(i);
+ continue;
}
+
+ final Task childTask = child.asTask();
+ final boolean inAdjacentTask = childTask != null
+ && child.inMultiWindowMode()
+ && childTask.getRootTask().getAdjacentTaskFragment() != null;
+
+ if (inAdjacentTask || child.inSplitScreenWindowingMode()) {
+ hasAdjacentTask = true;
+ } else if (hasAdjacentTask && startLayer < SPLIT_DIVIDER_LAYER) {
+ // Task on top of adjacent tasks should be higher than split divider layer so
+ // set it as start.
+ startLayer = SPLIT_DIVIDER_LAYER + 1;
+ }
+
+ child.assignLayer(t, startLayer++);
}
final int zBoostSize = mTmpNeedsZBoostIndexes.size();
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index d543c1f..b8ceb4a 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -818,7 +818,7 @@
final int layoutMinHeight = (layout == null) ? -1 : layout.minHeight;
// Aspect ratio requirements.
- final float minAspectRatio = info.getMinAspectRatio();
+ final float minAspectRatio = info.getMinAspectRatio(orientation);
final float maxAspectRatio = info.getMaxAspectRatio();
final int width = Math.min(defaultWidth, Math.max(phoneWidth, layoutMinWidth));
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 96c935a..63246ac 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -392,7 +392,7 @@
final WindowState imeWindow = task.getDisplayContent().mInputMethodWindow;
SurfaceControl.ScreenshotHardwareBuffer imeBuffer = null;
if (imeWindow != null && imeWindow.isWinVisibleLw()) {
- final Rect bounds = imeWindow.getContainingFrame();
+ final Rect bounds = imeWindow.getParentFrame();
bounds.offsetTo(0, 0);
imeBuffer = SurfaceControl.captureLayersExcluding(imeWindow.getSurfaceControl(),
bounds, 1.0f, pixelFormat, null);
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index d93b649..08b1a2f 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -97,12 +97,6 @@
// Time we wait after a timeout before trying to wait again.
private static final long WALLPAPER_TIMEOUT_RECOVERY = 10000;
- // Set to the wallpaper window we would like to hide once the transition animations are done.
- // This is useful in cases where we don't want the wallpaper to be hidden when the close app
- // is a wallpaper target and is done animating out, but the opening app isn't a wallpaper
- // target and isn't done animating in.
- WindowState mDeferredHideWallpaper = null;
-
// We give a wallpaper up to 500ms to finish drawing before playing app transitions.
private static final long WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION = 500;
private static final int WALLPAPER_DRAW_NORMAL = 0;
@@ -165,7 +159,8 @@
boolean needsShowWhenLockedWallpaper = false;
if ((w.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0
&& mService.mPolicy.isKeyguardLocked()
- && mService.mPolicy.isKeyguardOccluded()) {
+ && (mService.mPolicy.isKeyguardOccluded()
+ || mService.mPolicy.isKeyguardUnoccluding())) {
// The lowest show when locked window decides whether we need to put the wallpaper
// behind.
needsShowWhenLockedWallpaper = !isFullscreen(w.mAttrs)
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index baea854..c3d3c82 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static com.android.server.wm.WindowFramesProto.COMPAT_FRAME;
-import static com.android.server.wm.WindowFramesProto.CONTAINING_FRAME;
import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME;
import static com.android.server.wm.WindowFramesProto.FRAME;
import static com.android.server.wm.WindowFramesProto.PARENT_FRAME;
@@ -37,32 +36,16 @@
private static final StringBuilder sTmpSB = new StringBuilder();
/**
- * In most cases, this is the area of the entire screen.
- *
- * TODO(b/111611553): The name is unclear and most likely should be swapped with
- * {@link #mDisplayFrame}
- * TODO(b/111611553): In some cases, it also includes top insets, like for IME. Determine
- * whether this is still necessary to do.
+ * The frame to be referenced while applying gravity and MATCH_PARENT.
*/
public final Rect mParentFrame = new Rect();
/**
- * The entire screen area of the {@link Task} this window is in. Usually equal to the
- * screen area of the device.
- *
- * TODO(b/111611553): The name is unclear and most likely should be swapped with
- * {@link #mParentFrame}
+ * The bounds that the window should fit.
*/
public final Rect mDisplayFrame = new Rect();
/**
- * Similar to {@link #mDisplayFrame}
- *
- * TODO: Why is this different than mDisplayFrame
- */
- final Rect mContainingFrame = new Rect();
-
- /**
* "Real" frame that the application sees, in display coordinate space.
*/
final Rect mFrame = new Rect();
@@ -124,10 +107,6 @@
return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height());
}
- void offsetFrames(int layoutXDiff, int layoutYDiff) {
- mFrame.offset(layoutXDiff, layoutYDiff);
- }
-
/**
* Updates info about whether the size of the window has changed since last reported.
*
@@ -188,16 +167,13 @@
final long token = proto.start(fieldId);
mParentFrame.dumpDebug(proto, PARENT_FRAME);
mDisplayFrame.dumpDebug(proto, DISPLAY_FRAME);
- mContainingFrame.dumpDebug(proto, CONTAINING_FRAME);
mFrame.dumpDebug(proto, FRAME);
mCompatFrame.dumpDebug(proto, COMPAT_FRAME);
proto.end(token);
}
public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "Frames: containing="
- + mContainingFrame.toShortString(sTmpSB)
- + " parent=" + mParentFrame.toShortString(sTmpSB)
+ pw.println(prefix + "Frames: parent=" + mParentFrame.toShortString(sTmpSB)
+ " display=" + mDisplayFrame.toShortString(sTmpSB));
pw.println(prefix + "mFrame=" + mFrame.toShortString(sTmpSB)
+ " last=" + mLastFrame.toShortString(sTmpSB));
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c4a5183..9c81377 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -169,10 +169,8 @@
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Insets;
-import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.graphics.Region;
import android.hardware.configstore.V1_0.OptionalBool;
import android.hardware.configstore.V1_1.DisplayOrientation;
@@ -343,8 +341,6 @@
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowManagerService" : TAG_WM;
- private static final String WM_USE_BLAST_ADAPTER_FLAG = "wm_use_blast_adapter";
-
static final int LAYOUT_REPEAT_THRESHOLD = 4;
static final boolean PROFILE_ORIENTATION = false;
@@ -646,13 +642,7 @@
StrictModeFlash mStrictModeFlash;
EmulatorDisplayOverlay mEmulatorDisplayOverlay;
- final float[] mTmpFloats = new float[9];
final Rect mTmpRect = new Rect();
- final Rect mTmpRect2 = new Rect();
- final Rect mTmpRect3 = new Rect();
- final RectF mTmpRectF = new RectF();
-
- final Matrix mTmpTransform = new Matrix();
boolean mDisplayReady;
boolean mSafeMode;
@@ -1059,8 +1049,6 @@
public void focusChanged();
}
- final Configuration mTempConfiguration = new Configuration();
-
final HighRefreshRateDenylist mHighRefreshRateDenylist;
// Maintainer of a collection of all possible DisplayInfo for all configurations of the
@@ -1156,11 +1144,6 @@
void onAppFreezeTimeout();
}
- private static WindowManagerService sInstance;
- static WindowManagerService getInstance() {
- return sInstance;
- }
-
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm) {
@@ -1180,11 +1163,12 @@
displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory,
Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
+ final WindowManagerService[] wms = new WindowManagerService[1];
DisplayThread.getHandler().runWithScissors(() ->
- sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
+ wms[0] = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
atm, displayWindowSettingsProvider, transactionFactory, surfaceFactory,
surfaceControlFactory), 0);
- return sInstance;
+ return wms[0];
}
private void initPolicy() {
@@ -1783,6 +1767,7 @@
win.mToken.addWindow(win);
displayPolicy.addWindowLw(win, attrs);
+ displayPolicy.setDropInputModePolicy(win, win.mAttrs);
if (type == TYPE_APPLICATION_STARTING && activity != null) {
activity.attachStartingWindow(win);
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
@@ -2515,9 +2500,11 @@
}
win.mInRelayout = false;
- if (mUseBLASTSync && win.useBLASTSync() && viewVisibility != View.GONE) {
+ if (mUseBLASTSync && win.useBLASTSync() && viewVisibility != View.GONE
+ && win.mNextRelayoutUseSync) {
win.prepareDrawHandlers();
win.markRedrawForSyncReported();
+ win.mNextRelayoutUseSync = false;
result |= RELAYOUT_RES_BLAST_SYNC;
}
@@ -3014,15 +3001,6 @@
aspectRatio);
}
- void getRootTaskBounds(int windowingMode, int activityType, Rect bounds) {
- final Task rootTask = mRoot.getRootTask(windowingMode, activityType);
- if (rootTask != null) {
- rootTask.getBounds(bounds);
- return;
- }
- bounds.setEmpty();
- }
-
/**
* Notifies window manager that {@link DisplayPolicy#isShowingDreamLw} has changed.
*/
@@ -3080,6 +3058,11 @@
syncInputTransactions(true /* waitForAnimations */);
}
+ @Override
+ public boolean isAppTransitionStateIdle() {
+ return getDefaultDisplayContentLocked().mAppTransition.isIdle();
+ }
+
/**
* Notifies activity manager that some Keyguard flags have changed and that it needs to
* reevaluate the visibilities of the activities.
@@ -6989,12 +6972,6 @@
}
}
- void intersectDisplayInsetBounds(Rect display, Rect insets, Rect inOutBounds) {
- mTmpRect3.set(display);
- mTmpRect3.inset(insets);
- inOutBounds.intersect(mTmpRect3);
- }
-
MousePositionTracker mMousePositionTracker = new MousePositionTracker();
private static class MousePositionTracker implements PointerEventListener {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d68a8ea..6ba21d5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -38,10 +38,9 @@
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
-import static android.view.WindowInsets.Type.displayCutout;
-import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+import static android.view.WindowLayout.UNSPECIFIED_LENGTH;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
@@ -49,7 +48,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
@@ -60,7 +58,6 @@
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NOT_MAGNIFIABLE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
@@ -224,7 +221,6 @@
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayInfo;
-import android.view.Gravity;
import android.view.IWindow;
import android.view.IWindowFocusObserver;
import android.view.IWindowId;
@@ -367,6 +363,7 @@
private boolean mDragResizingChangeReported = true;
private int mResizeMode;
private boolean mRedrawForSyncReported;
+ boolean mNextRelayoutUseSync;
/**
* {@code true} when the client was still drawing for sync when the sync-set was finished or
@@ -473,9 +470,6 @@
/** The frames used to compute a temporal layout appearance. */
private WindowFrames mSimulatedWindowFrames;
- /** Usually the same as {@link #getBounds()}. */
- private final Rect mInsetFrame = new Rect();
-
/**
* List of rects where system gestures should be ignored.
*
@@ -657,7 +651,6 @@
private PowerManager.WakeLock mDrawLock;
private final Rect mTmpRect = new Rect();
- private final Rect mTmpRect2 = new Rect();
private final Point mTmpPoint = new Point();
private final Transaction mTmpTransaction;
@@ -1138,10 +1131,10 @@
mWinAnimator = new WindowStateAnimator(this);
mWinAnimator.mAlpha = a.alpha;
- mRequestedWidth = 0;
- mRequestedHeight = 0;
- mLastRequestedWidth = 0;
- mLastRequestedHeight = 0;
+ mRequestedWidth = UNSPECIFIED_LENGTH;
+ mRequestedHeight = UNSPECIFIED_LENGTH;
+ mLastRequestedWidth = UNSPECIFIED_LENGTH;
+ mLastRequestedHeight = UNSPECIFIED_LENGTH;
mLayer = 0;
mOverrideScale = mWmService.mAtmService.mCompatModePackages.getCompatScale(
mAttrs.packageName, s.mUid);
@@ -1235,145 +1228,26 @@
return mOwnerCanAddInternalSystemWindow;
}
- /**
- * Returns {@code true} if the window owner has the permission to acquire a sleep token when
- * it's visible. That is, they have the permission
- * {@link androidManifest.permission#DEVICE_POWER}.
- */
- boolean canAcquireSleepToken() {
- return mSession.mCanAcquireSleepToken;
- }
-
- /**
- * Subtracts the insets calculated by intersecting {@param layoutFrame} with {@param insetFrame}
- * from {@param frame}. In other words, it applies the insets that would result if
- * {@param frame} would be shifted to {@param layoutFrame} and then applying the insets from
- * {@param insetFrame}. Also it respects {@param displayFrame} in case window has minimum
- * width/height applied and insets should be overridden.
- */
- private void subtractInsets(Rect frame, Rect layoutFrame, Rect insetFrame, Rect displayFrame) {
- final int left = Math.max(0, insetFrame.left - Math.max(layoutFrame.left, displayFrame.left));
- final int top = Math.max(0, insetFrame.top - Math.max(layoutFrame.top, displayFrame.top));
- final int right = Math.max(0, Math.min(layoutFrame.right, displayFrame.right) - insetFrame.right);
- final int bottom = Math.max(0, Math.min(layoutFrame.bottom, displayFrame.bottom) - insetFrame.bottom);
- frame.inset(left, top, right, bottom);
- }
-
- void computeFrameAndUpdateSourceFrame(DisplayFrames displayFrames) {
- computeFrame(displayFrames);
- // Update the source frame to provide insets to other windows during layout. If the
- // simulated frames exist, then this is not computing a stable result so just skip.
- if (mControllableInsetProvider != null && mSimulatedWindowFrames == null) {
- mControllableInsetProvider.updateSourceFrame();
- }
- }
-
- /**
- * Perform standard frame computation. The result can be obtained with getFrame() if so desired.
- */
- void computeFrame(DisplayFrames displayFrames) {
+ boolean skipLayout() {
if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
// removed or we are still waiting for some information. Because of this we don't
// want to apply any more changes to it, so it remains in this state until new window
// appears.
- return;
+ return true;
}
+ // Skip layout of the window when in transition to pip mode.
+ return mActivityRecord != null && mActivityRecord.mWaitForEnteringPinnedMode;
+ }
+
+ // TODO(b/161810301): Make the frame be passed from the client side.
+ void setFrame() {
+ // TODO(b/161810301): Set the window frame here. We don't have to do it now because
+ // DisplayPolicy has already done it for the window.
+
mHaveFrame = true;
- final Task task = getTask();
- final boolean isFullscreenAndFillsArea = !inMultiWindowMode() && matchesDisplayAreaBounds();
- final boolean windowsAreFloating = task != null && task.isFloating();
- final DisplayContent dc = getDisplayContent();
- final DisplayInfo displayInfo = getDisplayInfo();
- final WindowFrames windowFrames = getLayoutingWindowFrames();
-
- mInsetFrame.set(getBounds());
-
- // Denotes the actual frame used to calculate the insets and to perform the layout. When
- // resizing in docked mode, we'd like to freeze the layout, so we also need to freeze the
- // insets temporarily. By the notion of a task having a different layout frame, we can
- // achieve that while still moving the task around.
- final Rect layoutContainingFrame;
- final Rect layoutDisplayFrame;
-
- // The offset from the layout containing frame to the actual containing frame.
- final int layoutXDiff;
- final int layoutYDiff;
- final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow();
- final InsetsControlTarget imeTarget = dc.getImeTarget(IME_TARGET_LAYERING);
- final boolean isInputMethodAdjustTarget = windowsAreFloating
- ? imeTarget != null && task == imeTarget.getWindow().getTask()
- : isImeLayeringTarget();
- final boolean isImeTarget =
- imeWin != null && imeWin.isVisibleNow() && isInputMethodAdjustTarget;
- if (isFullscreenAndFillsArea || layoutInParentFrame()) {
- // We use the parent frame as the containing frame for fullscreen and child windows
- windowFrames.mContainingFrame.set(windowFrames.mParentFrame);
- layoutDisplayFrame = windowFrames.mDisplayFrame;
- layoutContainingFrame = windowFrames.mParentFrame;
- layoutXDiff = 0;
- layoutYDiff = 0;
- } else {
- windowFrames.mContainingFrame.set(getBounds());
- // IME is up and obscuring this window. Adjust the window position so it is visible.
- if (isImeTarget) {
- if (inFreeformWindowingMode()) {
- // Push the freeform window up to make room for the IME. However, don't push
- // it up past the bottom of the top bar.
- final InsetsState state = dc.getInsetsStateController().getRawInsetsState();
- final Rect visibleFrame = mTmpRect;
- visibleFrame.set(state.getDisplayFrame());
- visibleFrame.inset(state.calculateInsets(visibleFrame,
- systemBars() | ime() | displayCutout(), false /* ignoreVisibility */));
- final int bottomOverlap =
- windowFrames.mContainingFrame.bottom - visibleFrame.bottom;
- if (bottomOverlap > 0) {
- final int distanceToTop = Math.max(windowFrames.mContainingFrame.top
- - visibleFrame.top, 0);
- int offs = Math.min(bottomOverlap, distanceToTop);
- windowFrames.mContainingFrame.offset(0, -offs);
- mInsetFrame.offset(0, -offs);
- }
- } else if (!inPinnedWindowingMode() && windowFrames.mContainingFrame.bottom
- > windowFrames.mParentFrame.bottom) {
- // But in docked we want to behave like fullscreen and behave as if the task
- // were given smaller bounds for the purposes of layout. Skip adjustments for
- // the root pinned task, they are handled separately in the
- // PinnedTaskController.
- windowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom;
- }
- }
-
- if (windowsAreFloating) {
- // In floating modes (e.g. freeform, pinned) we have only to set the rectangle
- // if it wasn't set already. No need to intersect it with the (visible)
- // "content frame" since it is allowed to be outside the visible desktop.
- if (windowFrames.mContainingFrame.isEmpty()) {
- windowFrames.mContainingFrame.set(windowFrames.mDisplayFrame);
- }
- }
-
- layoutDisplayFrame = mTmpRect2;
- layoutDisplayFrame.set(windowFrames.mDisplayFrame);
- windowFrames.mDisplayFrame.set(windowFrames.mContainingFrame);
- layoutXDiff = mInsetFrame.left - windowFrames.mContainingFrame.left;
- layoutYDiff = mInsetFrame.top - windowFrames.mContainingFrame.top;
- layoutContainingFrame = mInsetFrame;
- mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
- subtractInsets(windowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
- mTmpRect);
- if (!layoutInParentFrame()) {
- subtractInsets(windowFrames.mContainingFrame, layoutContainingFrame,
- windowFrames.mParentFrame, mTmpRect);
- subtractInsets(mInsetFrame, layoutContainingFrame, windowFrames.mParentFrame,
- mTmpRect);
- }
- layoutDisplayFrame.intersect(layoutContainingFrame);
- }
-
- final int pw = windowFrames.mContainingFrame.width();
- final int ph = windowFrames.mContainingFrame.height();
+ final WindowFrames windowFrames = mWindowFrames;
if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
mLastRequestedWidth = mRequestedWidth;
@@ -1381,21 +1255,6 @@
windowFrames.setContentChanged(true);
}
- final int fw = windowFrames.mFrame.width();
- final int fh = windowFrames.mFrame.height();
-
- applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame,
- displayFrames);
-
- if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
- mMovedByResize = true;
- }
- }
-
- // Offset the actual frame by the amount layout frame is off.
- windowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
-
windowFrames.mCompatFrame.set(windowFrames.mFrame);
if (hasCompatScale()) {
// Also the scaled frame that we report to the app needs to be
@@ -1403,14 +1262,9 @@
windowFrames.mCompatFrame.scale(mInvGlobalScale);
}
- if (mIsWallpaper && (fw != windowFrames.mFrame.width()
- || fh != windowFrames.mFrame.height())) {
- dc.mWallpaperController.updateWallpaperOffset(this, false /* sync */);
- }
-
// Calculate relative frame
windowFrames.mRelFrame.set(windowFrames.mFrame);
- WindowContainer parent = getParent();
+ WindowContainer<?> parent = getParent();
int parentLeft = 0;
int parentTop = 0;
if (mIsChildWindow) {
@@ -1425,12 +1279,37 @@
windowFrames.mFrame.top - parentTop);
if (DEBUG_LAYOUT || DEBUG) {
+ final int pw = windowFrames.mParentFrame.width();
+ final int ph = windowFrames.mParentFrame.height();
Slog.v(TAG, "Resolving (mRequestedWidth="
- + mRequestedWidth + ", mRequestedheight="
- + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
- + "): frame=" + windowFrames.mFrame.toShortString()
- + " " + mAttrs.getTitle());
+ + mRequestedWidth + ", mRequestedheight="
+ + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
+ + "): frame=" + windowFrames.mFrame.toShortString()
+ + " " + mAttrs.getTitle());
}
+
+ if (mAttrs.type == TYPE_DOCK_DIVIDER) {
+ if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
+ mMovedByResize = true;
+ }
+ }
+
+ if (mIsWallpaper) {
+ final Rect lastFrame = windowFrames.mLastFrame;
+ final Rect frame = windowFrames.mFrame;
+ if (lastFrame.width() != frame.width() || lastFrame.height() != frame.height()) {
+ mDisplayContent.mWallpaperController.updateWallpaperOffset(this, false /* sync */);
+ }
+ }
+
+ // Update the source frame to provide insets to other windows during layout.
+ if (mControllableInsetProvider != null) {
+ mControllableInsetProvider.updateSourceFrame();
+ }
+ }
+
+ // TODO(b/161810301): Remove this after INSETS_LAYOUT_GENERALIZATION is removed.
+ void computeFrameAndUpdateSourceFrame(DisplayFrames displayFrames) {
}
@Override
@@ -1463,10 +1342,6 @@
return mWindowFrames.mParentFrame;
}
- Rect getContainingFrame() {
- return mWindowFrames.mContainingFrame;
- }
-
void getCompatFrameSize(Rect outFrame) {
outFrame.set(0, 0, mWindowFrames.mCompatFrame.width(), mWindowFrames.mCompatFrame.height());
}
@@ -1493,21 +1368,11 @@
return mDisableFlags;
}
- /** Gets the layer at which this window's surface will be Z-ordered. */
- int getSurfaceLayer() {
- return mLayer;
- }
-
@Override
public int getBaseType() {
return getTopParentWindow().mAttrs.type;
}
- /** Returns true if this window is participating in voice interaction. */
- boolean isVoiceInteraction() {
- return mActivityRecord != null && mActivityRecord.mVoiceInteraction;
- }
-
boolean setReportResizeHints() {
return mWindowFrames.setReportResizeHints();
}
@@ -1834,14 +1699,6 @@
&& (mActivityRecord.firstWindowDrawn || mActivityRecord.startingDisplayed);
}
- boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
- if (dsdx < .99999f || dsdx > 1.00001f) return false;
- if (dtdy < .99999f || dtdy > 1.00001f) return false;
- if (dtdx < -.000001f || dtdx > .000001f) return false;
- if (dsdy < -.000001f || dsdy > .000001f) return false;
- return true;
- }
-
void prelayout() {
if (hasCompatScale()) {
if (mOverrideScale != 1f) {
@@ -3441,15 +3298,6 @@
return mAnimatingExit || (mActivityRecord != null && mActivityRecord.isClosingOrEnteringPip());
}
- void addWinAnimatorToList(ArrayList<WindowStateAnimator> animators) {
- animators.add(mWinAnimator);
-
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowState c = mChildren.get(i);
- c.addWinAnimatorToList(animators);
- }
- }
-
void sendAppVisibilityToClients() {
super.sendAppVisibilityToClients();
@@ -4023,11 +3871,6 @@
return getDisplayContent().mCurrentFocus == this;
}
- /** Is this window in a container that takes up the entire screen space? */
- private boolean inAppWindowThatMatchesParentBounds() {
- return mActivityRecord == null || (mActivityRecord.matchParentBounds() && !inMultiWindowMode());
- }
-
/**
* @return true if activity bounds are letterboxed or letterboxed for diplay cutout.
*
@@ -4402,107 +4245,10 @@
return mStringNameCache;
}
- private void applyGravityAndUpdateFrame(WindowFrames windowFrames, Rect containingFrame,
- Rect displayFrame, DisplayFrames displayFrames) {
- final int pw = containingFrame.width();
- final int ph = containingFrame.height();
- final Task task = getTask();
- final boolean inNonFullscreenContainer = !inAppWindowThatMatchesParentBounds();
- final WindowManager.LayoutParams attrs = getLayoutingAttrs(displayFrames.mRotation);
- final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
-
- // We need to fit it to the display if either
- // a) The window is in a fullscreen container, or we don't have a task (we assume fullscreen
- // for the taskless windows)
- // b) If it's a secondary app window, we also need to fit it to the display unless
- // FLAG_LAYOUT_NO_LIMITS is set. This is so we place Popups, dialogs, and similar windows on
- // screen, but SurfaceViews want to be always at a specific location so we don't fit it to
- // the display.
- final boolean fitToDisplay = (task == null || !inNonFullscreenContainer)
- || ((attrs.type != TYPE_BASE_APPLICATION) && !noLimits);
- float x, y;
- int w,h;
-
- final boolean hasCompatScale = hasCompatScale();
- if ((attrs.flags & FLAG_SCALED) != 0 || mAttrs != attrs) {
- // For the window with different layout attrs for different rotations, we need to avoid
- // using requested size. Otherwise, when finishing a simulated rotation, the information
- // coming from WindowManagerServices to the ViewRootImpl may not contain the correct
- // value for the new rotation, and there will be a quick flash of wrong layout when the
- // simulated activity faded out.
- if (attrs.width < 0) {
- w = pw;
- } else if (hasCompatScale) {
- w = (int) (attrs.width * mGlobalScale + .5f);
- } else {
- w = attrs.width;
- }
- if (attrs.height < 0) {
- h = ph;
- } else if (hasCompatScale) {
- h = (int) (attrs.height * mGlobalScale + .5f);
- } else {
- h = attrs.height;
- }
- } else {
- if (attrs.width == MATCH_PARENT) {
- w = pw;
- } else if (hasCompatScale) {
- w = (int) (mRequestedWidth * mGlobalScale + .5f);
- } else {
- w = mRequestedWidth;
- }
- if (attrs.height == MATCH_PARENT) {
- h = ph;
- } else if (hasCompatScale) {
- h = (int) (mRequestedHeight * mGlobalScale + .5f);
- } else {
- h = mRequestedHeight;
- }
- }
-
- if (hasCompatScale) {
- x = attrs.x * mGlobalScale;
- y = attrs.y * mGlobalScale;
- } else {
- x = attrs.x;
- y = attrs.y;
- }
-
- if (inNonFullscreenContainer && !layoutInParentFrame()) {
- // Make sure window fits in containing frame since it is in a non-fullscreen task as
- // required by {@link Gravity#apply} call.
- w = Math.min(w, pw);
- h = Math.min(h, ph);
- }
-
- // Set mFrame
- Gravity.apply(attrs.gravity, w, h, containingFrame,
- (int) (x + attrs.horizontalMargin * pw),
- (int) (y + attrs.verticalMargin * ph), windowFrames.mFrame);
- // Now make sure the window fits in the overall display frame.
- if (fitToDisplay) {
- Gravity.applyDisplay(attrs.gravity, displayFrame, windowFrames.mFrame);
- }
-
- // We need to make sure we update the CompatFrame as it is used for
- // cropping decisions, etc, on systems where we lack a decor layer.
- windowFrames.mCompatFrame.set(windowFrames.mFrame);
- if (hasCompatScale) {
- // See comparable block in computeFrameLw.
- windowFrames.mCompatFrame.scale(mInvGlobalScale);
- }
- }
-
boolean isChildWindow() {
return mIsChildWindow;
}
- boolean layoutInParentFrame() {
- return mIsChildWindow
- && (mAttrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) != 0;
- }
-
/**
* Returns true if any window added by an application process that if of type
* {@link android.view.WindowManager.LayoutParams#TYPE_TOAST} or that requires that requires
@@ -4876,20 +4622,27 @@
private boolean applyImeWindowsIfNeeded(ToBooleanFunction<WindowState> callback,
boolean traverseTopToBottom) {
- // If this window is the current IME target, so we need to process the IME windows
- // directly above it. The exception is if we are in split screen
- // in which case we process the IME at the DisplayContent level to
- // ensure it is above the docked divider.
- // (i.e. Like {@link DisplayContent.ImeContainer#skipImeWindowsDuringTraversal}, the IME
- // window will be ignored to traverse when the IME target is still in split-screen mode).
- if (isImeLayeringTarget()
- && (!mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated()
- || getTask() == null)) {
- if (mDisplayContent.forAllImeWindows(callback, traverseTopToBottom)) {
- return true;
- }
+ // No need to apply to IME window if the window is not the current IME layering target.
+ if (!isImeLayeringTarget()) {
+ return false;
}
- return false;
+ // If we are in split screen which case we process the IME at the DisplayContent level to
+ // ensure it is above the docked divider.
+ // i.e. Like {@link DisplayContent.ImeContainer#skipImeWindowsDuringTraversal}, the IME
+ // window will be ignored to traverse when the IME target is still in split-screen mode.
+ if (mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated()
+ && getTask() != null) {
+ return false;
+ }
+ // Note that we don't process IME window if the IME input target is not on the screen.
+ // In case some unexpected IME visibility cases happen like starting the remote
+ // animation on the keyguard but seeing the IME window that originally on the app
+ // which behinds the keyguard.
+ final WindowState imeInputTarget = getImeInputTarget();
+ if (imeInputTarget != null && !(imeInputTarget.isDrawn() || imeInputTarget.isVisible())) {
+ return false;
+ }
+ return mDisplayContent.forAllImeWindows(callback, traverseTopToBottom);
}
private boolean applyInOrderWithImeWindows(ToBooleanFunction<WindowState> callback,
@@ -5877,7 +5630,7 @@
// animation target (which will be different than the task bounds)
outFrame.set(getTask().getParent().getBounds());
} else {
- outFrame.set(getContainingFrame());
+ outFrame.set(getParentFrame());
}
outSurfaceInsets.set(getAttrs().surfaceInsets);
final InsetsState state = getInsetsStateWithVisibilityOverride();
@@ -5907,6 +5660,7 @@
// to draw even if the children draw first or don't need to sync, so we start
// in WAITING state rather than READY.
mSyncState = SYNC_STATE_WAITING_FOR_DRAW;
+ mNextRelayoutUseSync = true;
requestRedrawForSync();
return true;
}
@@ -6088,6 +5842,7 @@
*/
void applyWithNextDraw(Consumer<SurfaceControl.Transaction> consumer) {
mPendingDrawHandlers.add(consumer);
+ mNextRelayoutUseSync = true;
requestRedrawForSync();
mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 423b3a0..b147455 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -118,9 +118,6 @@
* window is first added or shown, cleared when the callback has been made. */
boolean mEnteringAnimation;
- /** The pixel format of the underlying SurfaceControl */
- int mSurfaceFormat;
-
/** This is set when there is no Surface */
static final int NO_SURFACE = 0;
/** This is set after the Surface has been created but before the window has been drawn. During
@@ -347,8 +344,6 @@
mSurfaceController.setColorSpaceAgnostic((attrs.privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
- mSurfaceFormat = format;
-
w.setHasSurface(true);
// The surface instance is changed. Make sure the input info can be applied to the
// new surface, e.g. relaunch activity.
@@ -514,8 +509,6 @@
return;
}
- boolean displayed = false;
-
computeShownFrameLocked();
setSurfaceBoundariesLocked(t);
@@ -535,7 +528,6 @@
}
} else if (mLastAlpha != mShownAlpha
|| mLastHidden) {
- displayed = true;
mLastAlpha = mShownAlpha;
ProtoLog.i(WM_SHOW_TRANSACTIONS,
"SURFACE controller=%s alpha=%f HScale=%f, VScale=%f: %s",
@@ -566,14 +558,10 @@
}
}
}
- if (hasSurface()) {
- w.mToken.hasVisible = true;
- }
} else {
if (DEBUG_ANIM && mWin.isAnimating(TRANSITION | PARENTS)) {
Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
}
- displayed = true;
}
if (w.getOrientationChanging()) {
@@ -589,31 +577,6 @@
ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation change complete in %s", w);
}
}
-
- if (displayed) {
- w.mToken.hasVisible = true;
- }
- }
-
- /**
- * Try to change the pixel format without recreating the surface. This
- * will be common in the case of changing from PixelFormat.OPAQUE to
- * PixelFormat.TRANSLUCENT in the hardware-accelerated case as both
- * requested formats resolve to the same underlying SurfaceControl format
- * @return True if format was succesfully changed, false otherwise
- */
- boolean tryChangeFormatInPlaceLocked() {
- if (mSurfaceController == null) {
- return false;
- }
- final LayoutParams attrs = mWin.getAttrs();
- final boolean isHwAccelerated = (attrs.flags & FLAG_HARDWARE_ACCELERATED) != 0;
- final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
- if (format == mSurfaceFormat) {
- setOpaqueLocked(!PixelFormat.formatHasAlpha(attrs.format));
- return true;
- }
- return false;
}
void setOpaqueLocked(boolean isOpaque) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index ad351f0..34b63ac 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -26,8 +26,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
-import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.WINDOW_TOKEN;
@@ -102,9 +100,6 @@
// Is key dispatching paused for this token?
boolean paused = false;
- // Temporary for finding which tokens no longer have visible windows.
- boolean hasVisible;
-
// Set to true when this token is in a pending transaction where it
// will be shown.
boolean waitingToShow;
@@ -240,6 +235,7 @@
}
}
+ /** Starts exit animation or hides windows if needed. It is only used for non-activity token. */
void setExiting(boolean animateExit) {
if (isEmpty()) {
super.removeImmediately();
@@ -255,27 +251,15 @@
final int count = mChildren.size();
boolean changed = false;
- final boolean delayed = isAnimating(TRANSITION | PARENTS)
- || (isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION) && animateExit);
-
for (int i = 0; i < count; i++) {
final WindowState win = mChildren.get(i);
changed |= win.onSetAppExiting(animateExit);
}
- final ActivityRecord app = asActivityRecord();
- if (app != null) {
- app.setVisible(false);
- }
-
if (changed) {
mWmService.mWindowPlacerLocked.performSurfacePlacement();
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/);
}
-
- if (delayed) {
- mDisplayContent.mExitingTokens.add(this);
- }
}
/**
@@ -381,11 +365,6 @@
}
@Override
- public void onConfigurationChanged(Configuration newParentConfig) {
- super.onConfigurationChanged(newParentConfig);
- }
-
- @Override
void assignLayer(SurfaceControl.Transaction t, int layer) {
if (windowType == TYPE_DOCK_DIVIDER) {
// See {@link DisplayContent#mSplitScreenDividerAnchor}
@@ -723,7 +702,6 @@
super.dump(pw, prefix, dumpAll);
pw.print(prefix); pw.print("windows="); pw.println(mChildren);
pw.print(prefix); pw.print("windowType="); pw.print(windowType);
- pw.print(" hasVisible="); pw.print(hasVisible);
if (waitingToShow) {
pw.print(" waitingToShow=true");
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 4e4a5c3..eb2c8a6 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -114,6 +114,8 @@
"libutils",
"libui",
"libvibratorservice",
+ "PlatformProperties",
+ "InputFlingerProperties",
"libinput",
"libinputflinger",
"libinputflinger_base",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 94b1ad1..790acbf 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -26,30 +26,14 @@
// Log debug messages about InputDispatcherPolicy
#define DEBUG_INPUT_DISPATCHER_POLICY 0
+#include <InputFlingerProperties.sysprop.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android/os/IInputConstants.h>
+#include <android/sysprop/InputProperties.sysprop.h>
+#include <android_os_MessageQueue.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
-#include <limits.h>
-#include <atomic>
-#include <cinttypes>
-
-#include <utils/Log.h>
-#include <utils/Looper.h>
-#include <utils/threads.h>
-#include <utils/Trace.h>
-
-#include <binder/IServiceManager.h>
-
-#include <input/PointerController.h>
-#include <input/SpriteController.h>
-#include <ui/Region.h>
-
-#include <batteryservice/include/batteryservice/BatteryServiceConstants.h>
-#include <inputflinger/InputManager.h>
-
-#include <android_os_MessageQueue.h>
#include <android_view_InputChannel.h>
#include <android_view_InputDevice.h>
#include <android_view_KeyEvent.h>
@@ -57,11 +41,25 @@
#include <android_view_PointerIcon.h>
#include <android_view_VerifiedKeyEvent.h>
#include <android_view_VerifiedMotionEvent.h>
-
+#include <batteryservice/include/batteryservice/BatteryServiceConstants.h>
+#include <binder/IServiceManager.h>
+#include <input/PointerController.h>
+#include <input/SpriteController.h>
+#include <inputflinger/InputManager.h>
+#include <limits.h>
#include <nativehelper/ScopedLocalFrame.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
+#include <ui/Region.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+#include <utils/Trace.h>
+#include <utils/threads.h>
+
+#include <atomic>
+#include <cinttypes>
+#include <vector>
#include "android_hardware_display_DisplayViewport.h"
#include "android_hardware_input_InputApplicationHandle.h"
@@ -69,8 +67,6 @@
#include "android_util_Binder.h"
#include "com_android_server_power_PowerManagerService.h"
-#include <vector>
-
#define INDENT " "
using android::base::ParseUint;
@@ -2089,11 +2085,24 @@
InputReaderConfiguration::CHANGE_DEVICE_ALIAS);
}
-static jstring nativeDump(JNIEnv* env, jclass /* clazz */, jlong ptr) {
- NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+static std::string dumpInputProperties() {
+ std::string out = "Input properties:\n";
+ const bool perWindowInputRotation =
+ sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false);
+ out += StringPrintf(" per_window_input_rotation = %s\n", toString(perWindowInputRotation));
+ const std::string strategy =
+ sysprop::InputProperties::velocitytracker_strategy().value_or("default");
+ out += " persist.input.velocitytracker.strategy = " + strategy + "\n";
+ out += "\n";
+ return out;
+}
- std::string dump;
+static jstring nativeDump(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+ std::string dump = dumpInputProperties();
+
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
im->dump(dump);
+
return env->NewStringUTF(dump.c_str());
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f351a8c..ce03b59 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -251,6 +251,8 @@
"com.android.server.print.PrintManagerService";
private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS =
"com.android.server.companion.CompanionDeviceManagerService";
+ private static final String VIRTUAL_DEVICE_MANAGER_SERVICE_CLASS =
+ "com.android.server.companion.virtual.VirtualDeviceManagerService";
private static final String STATS_COMPANION_APEX_PATH =
"/apex/com.android.os.statsd/javalib/service-statsd.jar";
private static final String SCHEDULING_APEX_PATH =
@@ -2322,6 +2324,11 @@
t.traceBegin("StartCompanionDeviceManager");
mSystemServiceManager.startService(COMPANION_DEVICE_MANAGER_SERVICE_CLASS);
t.traceEnd();
+
+ // VirtualDeviceManager depends on CDM to control the associations.
+ t.traceBegin("StartVirtualDeviceManager");
+ mSystemServiceManager.startService(VIRTUAL_DEVICE_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
}
t.traceBegin("StartRestrictionManager");
diff --git a/services/tests/PackageManager/TEST_MAPPING b/services/tests/PackageManager/TEST_MAPPING
new file mode 100644
index 0000000..72d4c82
--- /dev/null
+++ b/services/tests/PackageManager/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "PackageInstallerTests"
+ }
+ ]
+}
+
diff --git a/services/tests/PackageManagerComponentOverrideTests/TEST_MAPPING b/services/tests/PackageManagerComponentOverrideTests/TEST_MAPPING
new file mode 100644
index 0000000..528c949
--- /dev/null
+++ b/services/tests/PackageManagerComponentOverrideTests/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "PackageManagerComponentOverrideTests"
+ }
+ ]
+}
+
diff --git a/services/tests/PackageManagerServiceTests/OWNERS b/services/tests/PackageManagerServiceTests/OWNERS
index 182dfe8..86ae581 100644
--- a/services/tests/PackageManagerServiceTests/OWNERS
+++ b/services/tests/PackageManagerServiceTests/OWNERS
@@ -1,3 +1 @@
-chiuwinson@google.com
-patb@google.com
-toddke@google.com
+include /PACKAGE_MANAGER_OWNERS
diff --git a/services/tests/PackageManagerServiceTests/TEST_MAPPING b/services/tests/PackageManagerServiceTests/TEST_MAPPING
new file mode 100644
index 0000000..af0008c
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "AppEnumerationInternalTests"
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "PackageManagerServiceHostTests"
+ }
+ ],
+ "imports": [
+ {
+ "path": "frameworks/base/services/tests/PackageManagerServiceTests/unit"
+ }
+ ]
+}
+
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 6a4d69f..b72f05f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -43,7 +43,6 @@
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
import android.content.pm.StagedApexInfo;
-import android.os.Message;
import android.os.SystemProperties;
import android.os.storage.IStorageManager;
import android.platform.test.annotations.Presubmit;
@@ -113,7 +112,7 @@
when(SystemProperties.get(eq("ro.apex.updatable"), anyString())).thenReturn("true");
mTmpDir = mTemporaryFolder.newFolder("StagingManagerTest");
- mStagingManager = new StagingManager(mContext, null, mApexManager);
+ mStagingManager = new StagingManager(mContext, mApexManager);
}
@After
@@ -123,32 +122,6 @@
}
}
- /**
- * Tests that sessions committed later shouldn't cause earlier ones to fail the overlapping
- * check.
- */
- @Test
- public void checkNonOverlappingWithStagedSessions_laterSessionShouldNotFailEarlierOnes()
- throws Exception {
- // Create 2 sessions with overlapping packages
- StagingManager.StagedSession session1 = createSession(111, "com.foo", 1);
- StagingManager.StagedSession session2 = createSession(222, "com.foo", 2);
-
- mStagingManager.createSession(session1);
- mStagingManager.createSession(session2);
- // Session1 should not fail in spite of the overlapping packages
- mStagingManager.checkNonOverlappingWithStagedSessions(session1);
- // setSessionFailed() should've been called when doing overlapping checks on session1
- verify(session2, times(1)).setSessionFailed(anyInt(), anyString());
-
- // Yet another session with overlapping packages
- StagingManager.StagedSession session3 = createSession(333, "com.foo", 3);
- mStagingManager.createSession(session3);
- assertThrows(PackageManagerException.class,
- () -> mStagingManager.checkNonOverlappingWithStagedSessions(session3));
- verify(session3, never()).setSessionFailed(anyInt(), anyString());
- }
-
@Test
public void restoreSessions_nonParentSession_throwsIAE() throws Exception {
FakeStagedSession session = new FakeStagedSession(239);
@@ -726,10 +699,9 @@
{
FakeStagedSession session = new FakeStagedSession(239);
session.setIsApex(true);
- mStagingManager.createSession(session);
-
+ session.setSessionReady();
mockApexManagerGetStagedApexInfoWithSessionId();
- triggerEndOfPreRebootVerification(session);
+ mStagingManager.commitSession(session);
assertThat(session.isSessionReady()).isTrue();
ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
@@ -744,9 +716,8 @@
Mockito.clearInvocations(observer);
FakeStagedSession session = new FakeStagedSession(240);
session.setIsApex(true);
- mStagingManager.createSession(session);
-
- triggerEndOfPreRebootVerification(session);
+ session.setSessionReady();
+ mStagingManager.commitSession(session);
assertThat(session.isSessionReady()).isTrue();
ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
@@ -762,9 +733,8 @@
Mockito.clearInvocations(observer);
FakeStagedSession session = new FakeStagedSession(241);
session.setIsApex(true);
- mStagingManager.createSession(session);
-
- triggerEndOfPreRebootVerification(session);
+ session.setSessionReady();
+ mStagingManager.commitSession(session);
assertThat(session.isSessionReady()).isTrue();
verify(observer, never()).onApexStaged(any());
@@ -800,21 +770,13 @@
// Trigger end of pre-reboot verification
FakeStagedSession session = new FakeStagedSession(239);
- mStagingManager.createSession(session);
+ session.setSessionReady();
+ mStagingManager.commitSession(session);
- triggerEndOfPreRebootVerification(session);
assertThat(session.isSessionReady()).isTrue();
verify(observer, never()).onApexStaged(any());
}
- private void triggerEndOfPreRebootVerification(StagingManager.StagedSession session) {
- StagingManager.PreRebootVerificationHandler handler =
- mStagingManager.mPreRebootVerificationHandler;
- Message msg = handler.obtainMessage(
- handler.MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId(), -1, session);
- handler.handleMessage(msg);
- }
-
private StagingManager.StagedSession createSession(int sessionId, String packageName,
long committedMillis) {
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
@@ -1071,9 +1033,6 @@
}
@Override
- public void notifyEndPreRebootVerification() {}
-
- @Override
public void verifySession() {
mVerificationStarted = true;
}
diff --git a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java b/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java
index cb12ba7..a2ecbc3 100644
--- a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java
@@ -47,7 +47,6 @@
@Mock BatteryService.HealthServiceWrapper.IHealthSupplier mHealthServiceSupplier;
BatteryService.HealthServiceWrapper mWrapper;
- private static final String HEALTHD = BatteryService.HealthServiceWrapper.INSTANCE_HEALTHD;
private static final String VENDOR = BatteryService.HealthServiceWrapper.INSTANCE_VENDOR;
@Override
@@ -117,7 +116,7 @@
@SmallTest
public void testWrapPreferVendor() throws Exception {
- initForInstances(VENDOR, HEALTHD);
+ initForInstances(VENDOR);
mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
waitHandlerThreadFinish();
verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR));
@@ -126,16 +125,6 @@
}
@SmallTest
- public void testUseHealthd() throws Exception {
- initForInstances(HEALTHD);
- mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
- waitHandlerThreadFinish();
- verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(HEALTHD));
- verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString());
- verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(HEALTHD));
- }
-
- @SmallTest
public void testNoService() throws Exception {
initForInstances("unrelated");
try {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index fb5c557..96af617 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -100,7 +100,11 @@
MagnificationAnimationCallback.class);
private final MagnificationInfoChangedCallback mRequestObserver = mock(
MagnificationInfoChangedCallback.class);
- final MessageCapturingHandler mMessageCapturingHandler = new MessageCapturingHandler(null);
+ private final MessageCapturingHandler mMessageCapturingHandler = new MessageCapturingHandler(
+ null);
+ private final MagnificationScaleProvider mScaleProvider = mock(
+ MagnificationScaleProvider.class);
+
ValueAnimator mMockValueAnimator;
ValueAnimator.AnimatorUpdateListener mTargetAnimationListener;
@@ -123,7 +127,7 @@
initMockWindowManager();
mFullScreenMagnificationController = new FullScreenMagnificationController(
- mMockControllerCtx, new Object(), mRequestObserver);
+ mMockControllerCtx, new Object(), mRequestObserver, mScaleProvider);
}
@After
@@ -412,12 +416,12 @@
MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter,
- FullScreenMagnificationController.MAX_SCALE);
+ MagnificationScaleProvider.MAX_SCALE);
MagnificationSpec endSpec = getMagnificationSpec(
- FullScreenMagnificationController.MAX_SCALE, offsets);
+ MagnificationScaleProvider.MAX_SCALE, offsets);
assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId,
- FullScreenMagnificationController.MAX_SCALE + 1.0f,
+ MagnificationScaleProvider.MAX_SCALE + 1.0f,
newCenter.x, newCenter.y, false, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
@@ -632,31 +636,6 @@
}
@Test
- public void testSetUserId_resetsOnlyIfIdChanges() {
- for (int i = 0; i < DISPLAY_COUNT; i++) {
- testSetUserId_resetsOnlyIfIdChanges(i);
- resetMockWindowManager();
- }
- }
-
- private void testSetUserId_resetsOnlyIfIdChanges(int displayId) {
- final int userId1 = 1;
- final int userId2 = 2;
-
- register(displayId);
- mFullScreenMagnificationController.setUserId(userId1);
- PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
- float scale = 2.0f;
- mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y,
- false, SERVICE_ID_1);
-
- mFullScreenMagnificationController.setUserId(userId1);
- assertTrue(mFullScreenMagnificationController.isMagnifying(displayId));
- mFullScreenMagnificationController.setUserId(userId2);
- assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
- }
-
- @Test
public void testResetIfNeeded_doesWhatItSays() {
for (int i = 0; i < DISPLAY_COUNT; i++) {
testResetIfNeeded_doesWhatItSays(i);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 6c32f7e..2060223 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -22,6 +22,8 @@
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.android.server.testutils.TestUtils.strictMock;
import static org.junit.Assert.assertFalse;
@@ -38,16 +40,15 @@
import android.animation.ValueAnimator;
import android.annotation.NonNull;
-import android.content.Context;
import android.graphics.PointF;
import android.os.Handler;
import android.os.Message;
+import android.testing.TestableContext;
import android.util.DebugUtils;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
-import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityManagerService;
@@ -60,6 +61,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -121,7 +123,6 @@
private static final int DISPLAY_0 = 0;
- private Context mContext;
FullScreenMagnificationController mFullScreenMagnificationController;
@Mock
MagnificationGestureHandler.Callback mMockCallback;
@@ -134,6 +135,9 @@
@Mock
AccessibilityTraceManager mMockTraceManager;
+ @Rule
+ public final TestableContext mContext = new TestableContext(getInstrumentation().getContext());
+
private OffsettableClock mClock;
private FullScreenMagnificationGestureHandler mMgh;
private TestHandler mHandler;
@@ -143,7 +147,6 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getContext();
final FullScreenMagnificationController.ControllerContext mockController =
mock(FullScreenMagnificationController.ControllerContext.class);
final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class);
@@ -157,14 +160,16 @@
when(mockController.getAnimationDuration()).thenReturn(1000L);
when(mockWindowManager.setMagnificationCallbacks(eq(DISPLAY_0), any())).thenReturn(true);
mFullScreenMagnificationController = new FullScreenMagnificationController(mockController,
- new Object(), mMagnificationInfoChangedCallback) {
+ new Object(), mMagnificationInfoChangedCallback,
+ new MagnificationScaleProvider(mContext)) {
@Override
public boolean magnificationRegionContains(int displayId, float x, float y) {
return true;
}
@Override
- void setForceShowMagnifiableBounds(int displayId, boolean show) {}
+ void setForceShowMagnifiableBounds(int displayId, boolean show) {
+ }
};
mFullScreenMagnificationController.register(DISPLAY_0);
mClock = new OffsettableClock.Stopped();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 2cb3d27..69061c1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -46,6 +46,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
+import android.testing.DexmakerShareClassLoaderRule;
import android.view.Display;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.MagnificationAnimationCallback;
@@ -58,6 +59,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -79,7 +81,8 @@
private static final float MAGNIFIED_CENTER_X = 100;
private static final float MAGNIFIED_CENTER_Y = 200;
private static final float DEFAULT_SCALE = 3f;
- private static final int CURRENT_USER_ID = UserHandle.USER_CURRENT;
+ private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
+ private static final int SECOND_USER_ID = CURRENT_USER_ID + 1;
private static final int MODE_WINDOW = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
private static final int MODE_FULLSCREEN =
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@@ -94,6 +97,7 @@
private Context mContext;
@Mock
private FullScreenMagnificationController mScreenMagnificationController;
+ private MagnificationScaleProvider mScaleProvider;
@Captor
private ArgumentCaptor<MagnificationAnimationCallback> mCallbackArgumentCaptor;
@@ -103,6 +107,11 @@
private MagnificationController mMagnificationController;
private FullScreenMagnificationControllerStubber mScreenMagnificationControllerStubber;
+ // To mock package-private class
+ @Rule
+ public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+ new DexmakerShareClassLoaderRule();
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -113,15 +122,17 @@
Settings.Secure.putFloatForUser(mMockResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, DEFAULT_SCALE,
CURRENT_USER_ID);
+ mScaleProvider = spy(new MagnificationScaleProvider(mContext));
mWindowMagnificationManager = Mockito.spy(
new WindowMagnificationManager(mContext, CURRENT_USER_ID,
- mock(WindowMagnificationManager.Callback.class), mTraceManager));
+ mock(WindowMagnificationManager.Callback.class), mTraceManager,
+ mScaleProvider));
mMockConnection = new MockWindowMagnificationConnection(true);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mScreenMagnificationControllerStubber = new FullScreenMagnificationControllerStubber(
mScreenMagnificationController);
mMagnificationController = spy(new MagnificationController(mService, new Object(), mContext,
- mScreenMagnificationController, mWindowMagnificationManager));
+ mScreenMagnificationController, mWindowMagnificationManager, mScaleProvider));
mMagnificationController.setMagnificationCapabilities(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
@@ -283,14 +294,16 @@
verify(mScreenMagnificationController).onDisplayRemoved(TEST_DISPLAY);
verify(mWindowMagnificationManager).onDisplayRemoved(TEST_DISPLAY);
+ verify(mScaleProvider).onDisplayRemoved(TEST_DISPLAY);
}
@Test
- public void updateUserIdIfNeeded_AllModulesAvailable_setUserId() {
- mMagnificationController.updateUserIdIfNeeded(CURRENT_USER_ID);
+ public void updateUserIdIfNeeded_AllModulesAvailable_disableMagnificationAndChangeUserId() {
+ mMagnificationController.updateUserIdIfNeeded(SECOND_USER_ID);
- verify(mScreenMagnificationController).setUserId(CURRENT_USER_ID);
- verify(mWindowMagnificationManager).setUserId(CURRENT_USER_ID);
+ verify(mScreenMagnificationController).resetAllIfNeeded(false);
+ verify(mWindowMagnificationManager).disableAllWindowMagnifiers();
+ verify(mScaleProvider).onUserChanged(SECOND_USER_ID);
}
@Test
@@ -575,6 +588,13 @@
verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
}
+ @Test
+ public void onUserRemoved_notifyScaleProvider() {
+ mMagnificationController.onUserRemoved(SECOND_USER_ID);
+
+ verify(mScaleProvider).onUserRemoved(SECOND_USER_ID);
+ }
+
private void setMagnificationEnabled(int mode) throws RemoteException {
setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
}
@@ -627,7 +647,8 @@
TEST_DISPLAY);
doAnswer(invocation -> mIsMagnifying).when(
mScreenMagnificationController).isForceShowMagnifiableBounds(TEST_DISPLAY);
- doAnswer(invocation -> mScale).when(mScreenMagnificationController).getPersistedScale();
+ doAnswer(invocation -> mScale).when(mScreenMagnificationController).getPersistedScale(
+ TEST_DISPLAY);
doAnswer(invocation -> mScale).when(mScreenMagnificationController).getScale(
TEST_DISPLAY);
doAnswer(invocation -> mCenterX).when(mScreenMagnificationController).getCenterX(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationScaleProviderTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationScaleProviderTest.java
new file mode 100644
index 0000000..9b392b2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationScaleProviderTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.accessibility.magnification;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.UserHandle;
+import android.testing.TestableContext;
+import android.view.Display;
+
+import com.android.compatibility.common.util.TestUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+/**
+ * Tests for {@link MagnificationScaleProvider}.
+ */
+public class MagnificationScaleProviderTest {
+
+ private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY + 1;
+ private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
+ private static final int SECOND_USER_ID = CURRENT_USER_ID + 1;
+
+ private static final float TEST_SCALE = 3;
+ private static final float DEFAULT_SCALE =
+ MagnificationScaleProvider.DEFAULT_MAGNIFICATION_SCALE;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(getInstrumentation().getContext());
+
+ private MagnificationScaleProvider mScaleProvider;
+
+ @Before
+ public void setUp() {
+ mScaleProvider = new MagnificationScaleProvider(mContext);
+ }
+
+ @Test
+ public void putScaleOnDefaultDisplay_getExpectedValue() throws Exception {
+ mScaleProvider.putScale(TEST_SCALE, Display.DEFAULT_DISPLAY);
+
+ TestUtils.waitUntil("settings value is not changed",
+ () -> Float.compare(mScaleProvider.getScale(Display.DEFAULT_DISPLAY),
+ TEST_SCALE) == 0);
+ }
+
+ @Test
+ public void putScaleOnTestDisplay_getExpectedValue() {
+ mScaleProvider.putScale(TEST_SCALE, TEST_DISPLAY);
+
+ assertEquals(TEST_SCALE, mScaleProvider.getScale(TEST_DISPLAY), 0);
+ }
+
+ @Test
+ public void onUserChanged_putScale_fallbackToDefaultScale() {
+ mScaleProvider.putScale(TEST_SCALE, TEST_DISPLAY);
+
+ mScaleProvider.onUserChanged(SECOND_USER_ID);
+ assertEquals(DEFAULT_SCALE, mScaleProvider.getScale(TEST_DISPLAY), 0);
+ }
+
+ @Test
+ public void onUserRemoved_setScaleOnSecondUser_fallbackToDefaultScale() {
+ mScaleProvider.onUserChanged(SECOND_USER_ID);
+ mScaleProvider.putScale(TEST_SCALE, TEST_DISPLAY);
+ mScaleProvider.onUserChanged(CURRENT_USER_ID);
+
+ mScaleProvider.onUserRemoved(SECOND_USER_ID);
+ // Assume the second user is created with the same id
+ mScaleProvider.onUserChanged(SECOND_USER_ID);
+
+ assertEquals(DEFAULT_SCALE, mScaleProvider.getScale(TEST_DISPLAY), 0);
+ }
+
+ @Test
+ public void onTestDisplayRemoved_setScaleOnTestDisplay_fallbackToDefaultScale() {
+ mScaleProvider.putScale(TEST_SCALE, TEST_DISPLAY);
+
+ mScaleProvider.onDisplayRemoved(TEST_DISPLAY);
+
+ assertEquals(DEFAULT_SCALE, mScaleProvider.getScale(TEST_DISPLAY), 0);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index 95f4327..1b8aff5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -21,10 +21,10 @@
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
-import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.RemoteException;
+import android.testing.TestableContext;
import android.util.DebugUtils;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -39,6 +39,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -67,7 +68,10 @@
public static final float DEFAULT_TAP_Y = 299;
private static final int DISPLAY_0 = MockWindowMagnificationConnection.TEST_DISPLAY;
- private Context mContext;
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
private WindowMagnificationManager mWindowMagnificationManager;
private MockWindowMagnificationConnection mMockConnection;
private WindowMagnificationGestureHandler mWindowMagnificationGestureHandler;
@@ -79,9 +83,9 @@
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getInstrumentation().getContext();
mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0,
- mock(WindowMagnificationManager.Callback.class), mMockTrace);
+ mock(WindowMagnificationManager.Callback.class), mMockTrace,
+ new MagnificationScaleProvider(mContext));
mMockConnection = new MockWindowMagnificationConnection();
mWindowMagnificationGestureHandler = new WindowMagnificationGestureHandler(
mContext, mWindowMagnificationManager, mMockTrace, mMockCallback,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index af6d40f..da881c4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -67,7 +67,7 @@
public class WindowMagnificationManagerTest {
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
- private static final int CURRENT_USER_ID = UserHandle.USER_CURRENT;
+ private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
private MockWindowMagnificationConnection mMockConnection;
@Mock
@@ -91,7 +91,7 @@
mResolver = new MockContentResolver();
mMockConnection = new MockWindowMagnificationConnection();
mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID,
- mMockCallback, mMockTrace);
+ mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext));
when(mContext.getContentResolver()).thenReturn(mResolver);
doAnswer((InvocationOnMock invocation) -> {
@@ -230,7 +230,7 @@
public void getPersistedScale() {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- assertEquals(mWindowMagnificationManager.getPersistedScale(), 2.5f);
+ assertEquals(mWindowMagnificationManager.getPersistedScale(TEST_DISPLAY), 2.5f);
}
@Test
@@ -264,7 +264,7 @@
mWindowMagnificationManager.setScale(TEST_DISPLAY, 10.0f);
assertEquals(mWindowMagnificationManager.getScale(TEST_DISPLAY),
- WindowMagnificationManager.MAX_SCALE);
+ MagnificationScaleProvider.MAX_SCALE);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index 0602a55..f009488 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -334,10 +334,8 @@
launchToken=${this.launchToken}
lockTaskLaunchMode=${this.lockTaskLaunchMode}
logo=${this.logo}
- maxAspectRatio=${this.maxAspectRatio}
maxRecents=${this.maxRecents}
metaData=${this.metaData.dumpToString()}
- minAspectRatio=${this.minAspectRatio}
name=${this.name}
nonLocalizedLabel=${
// Per b/184574333, v1 mistakenly trimmed the label. v2 fixed this, but for test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index 50ebffc31..d4420bd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -24,8 +24,9 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -41,7 +42,6 @@
import android.service.notification.NotificationListenerService;
import android.util.ArraySet;
import android.util.Pair;
-import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
@@ -355,4 +355,19 @@
.getDisallowedPackages()).isEmpty();
}
+ @Test
+ public void testHasAllowedListener() {
+ final int uid1 = 1, uid2 = 2;
+ // enable mCn1 but not mCn2 for uid1
+ mListeners.addApprovedList(mCn1.flattenToString(), uid1, true);
+
+ // verify that:
+ // the package for mCn1 has an allowed listener for uid1 and not uid2
+ assertTrue(mListeners.hasAllowedListener(mCn1.getPackageName(), uid1));
+ assertFalse(mListeners.hasAllowedListener(mCn1.getPackageName(), uid2));
+
+ // and that mCn2 has no allowed listeners for either user id
+ assertFalse(mListeners.hasAllowedListener(mCn2.getPackageName(), uid1));
+ assertFalse(mListeners.hasAllowedListener(mCn2.getPackageName(), uid2));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index f61d300..e4f889b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3708,6 +3708,8 @@
assertEquals(IMPORTANCE_LOW,
mService.getNotificationRecord(sbn.getKey()).getImportance());
+ assertEquals(IMPORTANCE_UNSPECIFIED, mBinderService.getPackageImportance(
+ sbn.getPackageName()));
nb = new Notification.Builder(mContext)
.setContentTitle("foo")
@@ -8354,4 +8356,53 @@
verify(mPermissionHelper, never()).hasPermission(anyInt());
verify(mPreferencesHelper, never()).getNotificationChannelsBypassingDnd(PKG, mUid);
}
+
+ @Test
+ public void testMatchesCallFilter_noPermissionShouldThrow() throws Exception {
+ // make sure a caller without listener access or read_contacts permission can't call
+ // matchesCallFilter.
+ when(mListeners.hasAllowedListener(anyString(), anyInt())).thenReturn(false);
+ doThrow(new SecurityException()).when(mContext).enforceCallingPermission(
+ eq("android.permission.READ_CONTACTS"), anyString());
+
+ try {
+ // shouldn't matter what we're passing in, if we get past this line fail immediately
+ ((INotificationManager) mService.mService).matchesCallFilter(null);
+ fail("call to matchesCallFilter with no permissions should fail");
+ } catch (SecurityException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void testMatchesCallFilter_hasListenerPermission() throws Exception {
+ // make sure a caller with only listener access and not read_contacts permission can call
+ // matchesCallFilter.
+ when(mListeners.hasAllowedListener(anyString(), anyInt())).thenReturn(true);
+ doThrow(new SecurityException()).when(mContext).enforceCallingPermission(
+ eq("android.permission.READ_CONTACTS"), anyString());
+
+ try {
+ ((INotificationManager) mService.mService).matchesCallFilter(null);
+ // pass, this is not a functionality test
+ } catch (SecurityException e) {
+ fail("call to matchesCallFilter with listener permissions should work");
+ }
+ }
+
+ @Test
+ public void testMatchesCallFilter_hasContactsPermission() throws Exception {
+ // make sure a caller with only read_contacts permission and not listener access can call
+ // matchesCallFilter.
+ when(mListeners.hasAllowedListener(anyString(), anyInt())).thenReturn(false);
+ doNothing().when(mContext).enforceCallingPermission(
+ eq("android.permission.READ_CONTACTS"), anyString());
+
+ try {
+ ((INotificationManager) mService.mService).matchesCallFilter(null);
+ // pass, this is not a functionality test
+ } catch (SecurityException e) {
+ fail("call to matchesCallFilter with listener permissions should work");
+ }
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index 3ec8729..016b579 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -21,6 +21,7 @@
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_SYSTEM;
@@ -123,6 +124,7 @@
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -663,4 +665,17 @@
verify(mWorkerHandler, never()).post(
any(NotificationManagerService.EnqueueNotificationRunnable.class));
}
+
+ @Test
+ public void testDefaultChannelDoesNotUpdateApp_postMigrationToPermissions() throws Exception {
+ final NotificationChannel defaultChannel = mBinderService.getNotificationChannel(
+ PKG_N_MR1, ActivityManager.getCurrentUser(), PKG_N_MR1,
+ NotificationChannel.DEFAULT_CHANNEL_ID);
+ defaultChannel.setImportance(IMPORTANCE_NONE);
+
+ mBinderService.updateNotificationChannelForPackage(PKG_N_MR1, mUid, defaultChannel);
+
+ verify(mPermissionHelper).setNotificationPermission(
+ PKG_N_MR1, ActivityManager.getCurrentUser(), false, true);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 5324ec5f..d90f91a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -4072,6 +4072,28 @@
assertTrue((channelA.getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0);
assertTrue((channelB.getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0);
assertTrue((channelC.getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0);
+ }
+ @Test
+ public void testDefaultChannelUpdatesApp_preMigrationToPermissions() throws Exception {
+ final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1,
+ UID_N_MR1,
+ NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ defaultChannel.setImportance(IMPORTANCE_NONE);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
+
+ assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_N_MR1, UID_N_MR1));
+ }
+
+ @Test
+ public void testDefaultChannelDoesNotUpdateApp_postMigrationToPermissions() throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1,
+ UID_N_MR1,
+ NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ defaultChannel.setImportance(IMPORTANCE_NONE);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
+
+ assertEquals(IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1, UID_N_MR1));
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 4957ab9..03132c4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -50,7 +50,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
@@ -107,9 +106,6 @@
@Before
public void setUp() throws Exception {
mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
- // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
- // changing those frames.
- doNothing().when(mWindow).computeFrame(any());
spyOn(mStatusBarWindow);
spyOn(mNavBarWindow);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 08312ef..575e082 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -36,12 +36,14 @@
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -699,6 +701,51 @@
}
}
+ @UseTestDisplay(addWindows = W_INPUT_METHOD)
+ @Test
+ public void testLaunchRemoteAnimationWithoutImeBehind() {
+ final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1");
+ final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2");
+
+ // Simulating win1 has shown IME and being IME layering/input target
+ mDisplayContent.setImeLayeringTarget(win1);
+ mDisplayContent.setImeInputTarget(win1);
+ mImeWindow.mWinAnimator.mSurfaceController = mock(WindowSurfaceController.class);
+ mImeWindow.mWinAnimator.hide(mDisplayContent.getPendingTransaction(), "test");
+ spyOn(mDisplayContent);
+ doReturn(true).when(mImeWindow.mWinAnimator.mSurfaceController).hasSurface();
+ doReturn(true).when(mImeWindow.mWinAnimator.mSurfaceController)
+ .prepareToShowInTransaction(any(), anyFloat());
+ makeWindowVisibleAndDrawn(mImeWindow);
+ assertTrue(mImeWindow.isOnScreen());
+ assertFalse(mImeWindow.isParentWindowHidden());
+
+ try {
+ // Simulating now win1 is being covered by the lockscreen which has no surface,
+ // and then launching an activity win2 with the remote animation
+ win1.mHasSurface = false;
+ mDisplayContent.mOpeningApps.add(win2.mActivityRecord);
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win2.mActivityRecord, new Point(50, 100), null,
+ new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
+
+ mDisplayContent.applySurfaceChangesTransaction();
+ mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN),
+ any(), any(), any(), any());
+ // Verify the IME window won't apply surface change transaction with forAllImeWindows
+ verify(mDisplayContent, never()).forAllImeWindows(any(), eq(true));
+ } catch (Exception e) {
+ // no-op
+ } finally {
+ mDisplayContent.mOpeningApps.clear();
+ }
+ }
+
private AnimationAdapter setupForNonAppTargetNavBar(int transit, boolean shouldAttachNavBar) {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index dd7ad21..73e571a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1123,6 +1123,98 @@
}
@Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ public void testOverrideMinAspectRatioScreenOrientationNotSetThenChangedToPortrait() {
+ // In this test, the activity's orientation isn't fixed to portrait, therefore the override
+ // isn't applied.
+
+ setUpDisplaySizeWithApp(1000, 1200);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override should have no effect
+ assertEquals(1200, activity.getBounds().height());
+ assertEquals(1000, activity.getBounds().width());
+
+ // After changing the orientation to portrait the override should be applied.
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ activity.clearSizeCompatMode();
+
+ // The per-package override forces the activity into a 3:2 aspect ratio
+ assertEquals(1200, activity.getBounds().height());
+ assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ public void testOverrideMinAspectRatioScreenOrientationLandscapeThenChangedToPortrait() {
+ // In this test, the activity's orientation isn't fixed to portrait, therefore the override
+ // isn't applied.
+
+ setUpDisplaySizeWithApp(1000, 1200);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override should have no effect
+ assertEquals(1200, activity.getBounds().height());
+ assertEquals(1000, activity.getBounds().width());
+
+ // After changing the orientation to portrait the override should be applied.
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ activity.clearSizeCompatMode();
+
+ // The per-package override forces the activity into a 3:2 aspect ratio
+ assertEquals(1200, activity.getBounds().height());
+ assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ public void testOverrideMinAspectRatioScreenOrientationPortraitThenChangedToUnspecified() {
+ setUpDisplaySizeWithApp(1000, 1200);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override forces the activity into a 3:2 aspect ratio
+ assertEquals(1200, activity.getBounds().height());
+ assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ activity.getBounds().width(), 0.5);
+
+ // After changing the orientation to landscape the override shouldn't be applied.
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+ activity.clearSizeCompatMode();
+
+ // The per-package override should have no effect
+ assertEquals(1200, activity.getBounds().height());
+ assertEquals(1000, activity.getBounds().width());
+ }
+
+ @Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioWithoutGlobalOverride() {
// In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
deleted file mode 100644
index 316309c..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Copyright (C) 2016 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.wm;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.view.InsetsState.ITYPE_IME;
-import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.junit.Assert.assertEquals;
-
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.view.DisplayInfo;
-import android.view.Gravity;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.InsetsVisibilities;
-import android.view.WindowManager;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-
-/**
- * Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery.
- *
- * Build/Install/Run:
- * atest WmTests:WindowFrameTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class WindowFrameTests extends WindowTestsBase {
-
- private DisplayContent mTestDisplayContent;
- private DisplayFrames mTestDisplayFrames;
-
- @Before
- public void setUp() throws Exception {
- DisplayInfo testDisplayInfo = new DisplayInfo(mDisplayInfo);
- testDisplayInfo.displayCutout = null;
- mTestDisplayContent = createNewDisplay(testDisplayInfo);
- mTestDisplayFrames = mTestDisplayContent.mDisplayFrames;
- }
-
- // Do not use this function directly in the tests below. Instead, use more explicit function
- // such as assertFlame().
- private void assertRect(Rect rect, int left, int top, int right, int bottom) {
- assertEquals(left, rect.left);
- assertEquals(top, rect.top);
- assertEquals(right, rect.right);
- assertEquals(bottom, rect.bottom);
- }
-
- private void assertFrame(WindowState w, Rect frame) {
- assertEquals(w.getFrame(), frame);
- }
-
- private void assertFrame(WindowState w, int left, int top, int right, int bottom) {
- assertRect(w.getFrame(), left, top, right, bottom);
- }
-
- private void assertRelFrame(WindowState w, int left, int top, int right, int bottom) {
- assertRect(w.getRelativeFrame(), left, top, right, bottom);
- }
-
- @Test
- public void testLayoutInFullscreenTask() {
- // fullscreen task doesn't use bounds for computeFrame
- WindowState w = createWindow();
- w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
-
- // With no insets or system decor all the frames incoming from PhoneWindowManager
- // are identical.
- final Rect pf = new Rect(0, 0, 1000, 1000);
-
- // Here the window has FILL_PARENT, FILL_PARENT
- // so we expect it to fill the entire available frame.
- w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame(mTestDisplayFrames);
- assertFrame(w, 0, 0, 1000, 1000);
- assertRelFrame(w, 0, 0, 1000, 1000);
-
- // It can select various widths and heights within the bounds.
- // Strangely the window attribute width is ignored for normal windows
- // and we use mRequestedWidth/mRequestedHeight
- w.mAttrs.width = 300;
- w.mAttrs.height = 300;
- w.computeFrame(mTestDisplayFrames);
- // Explicit width and height without requested width/height
- // gets us nothing.
- assertFrame(w, 0, 0, 0, 0);
-
- w.mRequestedWidth = 300;
- w.mRequestedHeight = 300;
- w.computeFrame(mTestDisplayFrames);
- // With requestedWidth/Height we can freely choose our size within the
- // parent bounds.
- assertFrame(w, 0, 0, 300, 300);
-
- // With FLAG_SCALED though, requestedWidth/height is used to control
- // the unscaled surface size, and mAttrs.width/height becomes the
- // layout controller.
- w.mAttrs.flags = WindowManager.LayoutParams.FLAG_SCALED;
- w.mRequestedHeight = -1;
- w.mRequestedWidth = -1;
- w.mAttrs.width = 100;
- w.mAttrs.height = 100;
- w.computeFrame(mTestDisplayFrames);
- assertFrame(w, 0, 0, 100, 100);
- w.mAttrs.flags = 0;
-
- // But sizes too large will be clipped to the containing frame
- w.mRequestedWidth = 1200;
- w.mRequestedHeight = 1200;
- w.computeFrame(mTestDisplayFrames);
- assertFrame(w, 0, 0, 1000, 1000);
-
- // Before they are clipped though windows will be shifted
- w.mAttrs.x = 300;
- w.mAttrs.y = 300;
- w.mRequestedWidth = 1000;
- w.mRequestedHeight = 1000;
- w.computeFrame(mTestDisplayFrames);
- assertFrame(w, 0, 0, 1000, 1000);
-
- // If there is room to move around in the parent frame the window will be shifted according
- // to gravity.
- w.mAttrs.x = 0;
- w.mAttrs.y = 0;
- w.mRequestedWidth = 300;
- w.mRequestedHeight = 300;
- w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
- w.computeFrame(mTestDisplayFrames);
- assertFrame(w, 700, 0, 1000, 300);
- assertRelFrame(w, 700, 0, 1000, 300);
- w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
- w.computeFrame(mTestDisplayFrames);
- assertFrame(w, 700, 700, 1000, 1000);
- assertRelFrame(w, 700, 700, 1000, 1000);
- // Window specified x and y are interpreted as offsets in the opposite
- // direction of gravity
- w.mAttrs.x = 100;
- w.mAttrs.y = 100;
- w.computeFrame(mTestDisplayFrames);
- assertFrame(w, 600, 600, 900, 900);
- assertRelFrame(w, 600, 600, 900, 900);
- }
-
- @Test
- public void testLayoutNonfullscreenTask() {
- removeGlobalMinSizeRestriction();
- final DisplayInfo displayInfo = mWm.getDefaultDisplayContentLocked().getDisplayInfo();
- final int logicalWidth = displayInfo.logicalWidth;
- final int logicalHeight = displayInfo.logicalHeight;
-
- final Rect taskBounds = new Rect(
- logicalWidth / 4, logicalHeight / 4, logicalWidth / 4 * 3, logicalHeight / 4 * 3);
- WindowState w = createWindow();
- final Task task = w.getTask();
- // Use split-screen because it is non-fullscreen, but also not floating
- task.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- task.setBounds(taskBounds);
- // The bounds we are requesting might be different from what the system resolved based on
- // other factors.
- final Rect resolvedTaskBounds = task.getBounds();
- w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
-
- final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
- final WindowFrames windowFrames = w.getWindowFrames();
- windowFrames.setFrames(pf, pf);
- w.computeFrame(mTestDisplayFrames);
- // For non fullscreen tasks the containing frame is based off the
- // task bounds not the parent frame.
- assertEquals(resolvedTaskBounds, w.getFrame());
- assertEquals(0, w.getRelativeFrame().left);
- assertEquals(0, w.getRelativeFrame().top);
-
- pf.set(0, 0, logicalWidth, logicalHeight);
- // We still produce insets against the containing frame the same way.
- final int cfRight = logicalWidth / 2;
- final int cfBottom = logicalHeight / 2;
- final Rect cf = new Rect(0, 0, cfRight, cfBottom);
- windowFrames.setFrames(pf, pf);
- w.computeFrame(mTestDisplayFrames);
- assertEquals(resolvedTaskBounds, w.getFrame());
- assertEquals(0, w.getRelativeFrame().left);
- assertEquals(0, w.getRelativeFrame().top);
- }
-
- @Test
- @FlakyTest(bugId = 137879065)
- public void testLayoutLetterboxedWindow() {
- // First verify task behavior in multi-window mode.
- final DisplayInfo displayInfo = mTestDisplayContent.getDisplayInfo();
- final int logicalWidth = displayInfo.logicalWidth;
- final int logicalHeight = displayInfo.logicalHeight;
-
- final int taskLeft = logicalWidth / 5;
- final int taskTop = logicalHeight / 5;
- final int taskRight = logicalWidth / 4 * 3;
- final int taskBottom = logicalHeight / 4 * 3;
- final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
- WindowState w = createWindow();
- final Task task = w.getTask();
- // Use split-screen because it is non-fullscreen, but also not floating
- task.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- task.setBounds(taskBounds);
- w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
-
- final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
- final WindowFrames windowFrames = w.getWindowFrames();
- windowFrames.setFrames(pf, pf);
- w.computeFrame(mTestDisplayFrames);
- // For non fullscreen tasks the containing frame is based off the
- // task bounds not the parent frame.
- assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
-
- // Now simulate switch to fullscreen for letterboxed app.
- final int xInset = logicalWidth / 10;
- final Rect cf = new Rect(xInset, 0, logicalWidth - xInset, logicalHeight);
- Configuration config = new Configuration(w.mActivityRecord.getRequestedOverrideConfiguration());
- config.windowConfiguration.setBounds(cf);
- config.windowConfiguration.setAppBounds(cf);
- w.mActivityRecord.onRequestedOverrideConfigurationChanged(config);
- pf.set(0, 0, logicalWidth, logicalHeight);
- task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- task.setBounds(null);
- windowFrames.setFrames(pf, pf);
- w.computeFrame(mTestDisplayFrames);
- assertFrame(w, cf);
- }
-
- @Test
- public void testFreeformContentInsets() {
- removeGlobalMinSizeRestriction();
- // fullscreen task doesn't use bounds for computeFrame
- WindowState w = createWindow();
- final Task task = w.getTask();
- w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
- task.setWindowingMode(WINDOWING_MODE_FREEFORM);
-
- // Make IME attach to the window and can produce insets.
- final DisplayContent dc = mTestDisplayContent;
- dc.setImeLayeringTarget(w);
- WindowState mockIme = mock(WindowState.class);
- Mockito.doReturn(true).when(mockIme).isVisibleNow();
- dc.mInputMethodWindow = mockIme;
- final InsetsState state = dc.getInsetsStateController().getRawInsetsState();
- final InsetsSource imeSource = state.getSource(ITYPE_IME);
- final Rect imeFrame = new Rect(state.getDisplayFrame());
- imeFrame.top = 400;
- imeSource.setFrame(imeFrame);
- imeSource.setVisible(true);
- final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
- requestedVisibilities.setVisibility(ITYPE_IME, true);
- w.setRequestedVisibilities(requestedVisibilities);
- w.mAboveInsetsState.addSource(imeSource);
-
- // With no insets or system decor all the frames incoming from PhoneWindowManager
- // are identical.
- final Rect pf = new Rect(0, 0, 1000, 800);
-
- // First check that it only gets moved up enough to show window.
- final Rect winRect = new Rect(200, 200, 300, 500);
- task.setBounds(winRect);
- w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame(mTestDisplayFrames);
- assertFrame(w, winRect.left, imeFrame.top - winRect.height(), winRect.right, imeFrame.top);
-
- // Now check that it won't get moved beyond the top
- winRect.bottom = 650;
- task.setBounds(winRect);
- w.setBounds(winRect);
- w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame(mTestDisplayFrames);
- assertFrame(w, winRect.left, 0, winRect.right, winRect.height());
-
- // Now we have status bar. Check that it won't go into the status bar area.
- final Rect statusBarFrame = new Rect(state.getDisplayFrame());
- statusBarFrame.bottom = 60;
- state.getSource(ITYPE_STATUS_BAR).setFrame(statusBarFrame);
- w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame(mTestDisplayFrames);
- assertFrame(w, winRect.left, statusBarFrame.bottom, winRect.right,
- statusBarFrame.bottom + winRect.height());
-
- // Check that it's moved back without ime insets
- state.removeSource(ITYPE_IME);
- w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame(mTestDisplayFrames);
- assertEquals(winRect, w.getFrame());
- }
-
- private WindowState createWindow() {
- final WindowState ws = createWindow(null, TYPE_APPLICATION, mTestDisplayContent, "WindowFrameTests");
- spyOn(ws);
- return ws;
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java
new file mode 100644
index 0000000..117f2ff
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java
@@ -0,0 +1,158 @@
+/*
+ * 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.wm;
+
+import static android.view.WindowLayout.UNSPECIFIED_LENGTH;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.WindowConfiguration;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.Gravity;
+import android.view.InsetsState;
+import android.view.InsetsVisibilities;
+import android.view.WindowLayout;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link WindowLayout#computeWindowFrames}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:WindowLayoutTests
+ */
+@SmallTest
+@Presubmit
+public class WindowLayoutTests {
+ private static final int DISPLAY_WIDTH = 500;
+ private static final int DISPLAY_HEIGHT = 1000;
+ private static final int STATUS_BAR_HEIGHT = 10;
+ private static final int NAVIGATION_BAR_HEIGHT = 15;
+
+ private final WindowLayout mWindowLayout = new WindowLayout();
+ private final Rect mDisplayFrame = new Rect();
+ private final Rect mParentFrame = new Rect();
+ private final Rect mFrame = new Rect();
+
+ private WindowManager.LayoutParams mAttrs;
+ private InsetsState mState;
+ private final Rect mDisplayCutoutSafe = new Rect();
+ private Rect mWindowBounds;
+ private int mWindowingMode;
+ private int mRequestedWidth;
+ private int mRequestedHeight;
+ private InsetsVisibilities mRequestedVisibilities;
+ private Rect mAttachedWindowFrame;
+ private float mCompatScale;
+
+ @Before
+ public void setUp() {
+ mAttrs = new WindowManager.LayoutParams();
+ mState = new InsetsState();
+ mState.setDisplayFrame(new Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
+ mState.getSource(InsetsState.ITYPE_STATUS_BAR).setFrame(
+ 0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT);
+ mState.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setFrame(
+ 0, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT);
+ mState.getDisplayCutoutSafe(mDisplayCutoutSafe);
+ mWindowBounds = new Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
+ mWindowingMode = WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+ mRequestedWidth = DISPLAY_WIDTH;
+ mRequestedHeight = DISPLAY_HEIGHT;
+ mRequestedVisibilities = new InsetsVisibilities();
+ mAttachedWindowFrame = null;
+ mCompatScale = 1f;
+ }
+
+ @Test
+ public void testDefaultLayoutParams() {
+ computeWindowFrames();
+
+ assertRect(0, STATUS_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
+ mDisplayFrame);
+ assertRect(0, STATUS_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
+ mParentFrame);
+ assertRect(0, STATUS_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
+ mFrame);
+ }
+
+ @Test
+ public void testUnmeasured() {
+ mRequestedWidth = UNSPECIFIED_LENGTH;
+ mRequestedHeight = UNSPECIFIED_LENGTH;
+ computeWindowFrames();
+
+ assertRect(0, STATUS_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
+ mDisplayFrame);
+ assertRect(0, STATUS_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
+ mParentFrame);
+ assertRect(0, STATUS_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
+ mFrame);
+ }
+
+ @Test
+ public void testUnmeasuredWithSizeSpecifiedInLayoutParams() {
+ final int width = DISPLAY_WIDTH / 2;
+ final int height = DISPLAY_HEIGHT / 2;
+ mRequestedWidth = UNSPECIFIED_LENGTH;
+ mRequestedHeight = UNSPECIFIED_LENGTH;
+ mAttrs.width = width;
+ mAttrs.height = height;
+ mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
+ computeWindowFrames();
+
+ assertRect(0, STATUS_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
+ mDisplayFrame);
+ assertRect(0, STATUS_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
+ mParentFrame);
+ assertRect(0, STATUS_BAR_HEIGHT, width, STATUS_BAR_HEIGHT + height,
+ mFrame);
+ }
+
+ @Test
+ public void testNonFullscreenWindowBounds() {
+ final int top = Math.max(DISPLAY_HEIGHT / 2, STATUS_BAR_HEIGHT);
+ mWindowBounds.set(0, top, DISPLAY_WIDTH, DISPLAY_HEIGHT);
+ mRequestedWidth = UNSPECIFIED_LENGTH;
+ mRequestedHeight = UNSPECIFIED_LENGTH;
+ computeWindowFrames();
+
+ assertRect(0, top, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
+ mDisplayFrame);
+ assertRect(0, top, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
+ mParentFrame);
+ assertRect(0, top, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
+ mFrame);
+ }
+
+ // TODO(b/161810301): Move tests here from DisplayPolicyLayoutTests and add more tests.
+
+ private void computeWindowFrames() {
+ mWindowLayout.computeWindowFrames(mAttrs, mState, mDisplayCutoutSafe, mWindowBounds,
+ mWindowingMode, mRequestedWidth, mRequestedHeight, mRequestedVisibilities,
+ mAttachedWindowFrame, mCompatScale, mDisplayFrame, mParentFrame, mFrame);
+ }
+
+ private void assertRect(int left, int top, int right, int bottom, Rect actual) {
+ assertEquals(new Rect(left, top, right, bottom), actual);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index ac61bb1..056a07a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -285,8 +285,6 @@
mStatusBarWindow.mAttrs.gravity = Gravity.TOP;
mStatusBarWindow.mAttrs.layoutInDisplayCutoutMode =
LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mStatusBarWindow.setRequestedSize(WindowManager.LayoutParams.MATCH_PARENT,
- STATUS_BAR_HEIGHT);
}
}
if (addAll || ArrayUtils.contains(requestedWindows, W_NOTIFICATION_SHADE)) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 6c3a1f6..dfbdede 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -23,7 +23,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -469,24 +468,42 @@
@Test
public void testDockedDividerPosition() {
- final WindowState pinnedStackWindow = createWindow(null, WINDOWING_MODE_PINNED,
- ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
- "pinnedStackWindow");
- final WindowState splitScreenWindow = createWindow(null,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
- mDisplayContent, "splitScreenWindow");
- final WindowState splitScreenSecondaryWindow = createWindow(null,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
- TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow");
- final WindowState assistantStackWindow = createWindow(null,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
- mDisplayContent, "assistantStackWindow");
+ final Task pinnedTask =
+ createTask(mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+ final WindowState pinnedWindow =
+ createAppWindow(pinnedTask, ACTIVITY_TYPE_STANDARD, "pinnedWindow");
+
+ final Task belowTask =
+ createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState belowTaskWindow =
+ createAppWindow(belowTask, ACTIVITY_TYPE_STANDARD, "belowTaskWindow");
+
+ final Task splitScreenTask1 =
+ createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ final WindowState splitWindow1 =
+ createAppWindow(splitScreenTask1, ACTIVITY_TYPE_STANDARD, "splitWindow1");
+ final Task splitScreenTask2 =
+ createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ final WindowState splitWindow2 =
+ createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2");
+ splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2);
+ splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1);
+
+ final Task aboveTask =
+ createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState aboveTaskWindow =
+ createAppWindow(aboveTask, ACTIVITY_TYPE_STANDARD, "aboveTaskWindow");
mDisplayContent.assignChildLayers(mTransaction);
- assertWindowHigher(mDockedDividerWindow, splitScreenWindow);
- assertWindowHigher(mDockedDividerWindow, splitScreenSecondaryWindow);
- assertWindowHigher(pinnedStackWindow, mDockedDividerWindow);
+ assertWindowHigher(splitWindow1, belowTaskWindow);
+ assertWindowHigher(splitWindow1, belowTaskWindow);
+ assertWindowHigher(splitWindow2, belowTaskWindow);
+ assertWindowHigher(splitWindow2, belowTaskWindow);
+ assertWindowHigher(mDockedDividerWindow, splitWindow1);
+ assertWindowHigher(mDockedDividerWindow, splitWindow2);
+ assertWindowHigher(aboveTaskWindow, mDockedDividerWindow);
+ assertWindowHigher(pinnedWindow, aboveTaskWindow);
}
@Test
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 7a424c8..10fba60 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -650,7 +650,13 @@
private static boolean checkCarrierPrivilegeForAnySubId(Context context, int uid) {
SubscriptionManager sm = (SubscriptionManager) context.getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE);
- int[] activeSubIds = sm.getCompleteActiveSubscriptionIdList();
+ int[] activeSubIds;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ activeSubIds = sm.getCompleteActiveSubscriptionIdList();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
for (int activeSubId : activeSubIds) {
if (getCarrierPrivilegeStatus(context, activeSubId, uid)
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index edfb7bf..22eb50a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1886,6 +1886,20 @@
"lte_plus_threshold_bandwidth_khz_int";
/**
+ * The combined channel bandwidth threshold (non-inclusive) in KHz required to display the
+ * NR advanced (i.e. 5G+) data icon. It is 0 by default, meaning minimum bandwidth check is
+ * not enabled. Other factors like bands or frequency can also determine whether the NR
+ * advanced data icon is shown or not.
+ *
+ * @see #KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY
+ * @see #KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT
+ *
+ * @hide
+ */
+ public static final String KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT =
+ "nr_advanced_threshold_bandwidth_khz_int";
+
+ /**
* The string is used to filter redundant string from PLMN Network Name that's supplied by
* specific carrier.
*
@@ -3550,6 +3564,17 @@
"nr_advanced_capable_pco_id_int";
/**
+ * Enabled NR advanced (i.e. 5G+) icon while roaming. The default value is {@code true}, meaming
+ * the same NR advanced logic used for home network will be used for roaming network as well.
+ * Set this to {@code false} will disable NR advanced icon while the device is roaming,
+ * regardless meeting NR advanced criteria or not.
+ *
+ * @hide
+ */
+ public static final String KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL =
+ "enable_nr_advanced_for_roaming_bool";
+
+ /**
* This configuration allows the framework to use user data communication to detect Idle state,
* and this is used on the 5G icon.
*
@@ -5227,6 +5252,16 @@
public static final String KEY_VONR_SETTING_VISIBILITY_BOOL = "vonr_setting_visibility_bool";
/**
+ * Flag specifying whether VoNR should be enabled for carrier.
+ * If true, VoNr will be enabled. If false, hard disabled.
+ *
+ * Disabled by default.
+ *
+ * @hide
+ */
+ public static final String KEY_VONR_ENABLED_BOOL = "vonr_enabled_bool";
+
+ /**
* Determine whether unthrottle data retry when tracking area code (TAC/LAC) from cell changes
*
* @hide
@@ -5622,6 +5657,7 @@
sDefaults.putString(KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING, "");
sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true);
sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
+ sDefaults.putInt(KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 0);
sDefaults.putIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
new int[]{CARRIER_NR_AVAILABILITY_NSA, CARRIER_NR_AVAILABILITY_SA});
sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);
@@ -5717,6 +5753,7 @@
sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000);
sDefaults.putIntArray(KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY, new int[0]);
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.putBoolean(KEY_UNMETERED_NR_NSA_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false);
@@ -5856,6 +5893,7 @@
sDefaults.putBoolean(KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL, false);
sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false);
sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, false);
+ sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 3b44a34..d5315ac 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -949,6 +949,15 @@
public static final String VOIMS_OPT_IN_STATUS = SimInfo.COLUMN_VOIMS_OPT_IN_STATUS;
/**
+ * TelephonyProvider column name for NR Advanced calling
+ * Determines if the user has enabled VoNR settings for this subscription.
+ *
+ * @hide
+ */
+ public static final String NR_ADVANCED_CALLING_ENABLED =
+ SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED;
+
+ /**
* Profile class of the subscription
* @hide
*/
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a4afea1..9c52220 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2951,7 +2951,12 @@
* currently in use on the device for data transmission.
*
* If this object has been created with {@link #createForSubscriptionId}, applies to the given
- * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ * subId. Otherwise, applies to {@link SubscriptionManager#getActiveDataSubscriptionId()}.
+ *
+ * Note: Before {@link SubscriptionManager#getActiveDataSubscriptionId()} was introduced in API
+ * level 30, it was applied to {@link SubscriptionManager#getDefaultDataSubscriptionId()} which
+ * may be different now from {@link SubscriptionManager#getActiveDataSubscriptionId()}, e.g.
+ * when opportunistic network is providing cellular internet connection to the user.
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index e890acb..9572154 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -36,6 +36,7 @@
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -152,16 +153,9 @@
throw new RuntimeException(
"Failed to find NetworkScanInfo with id " + message.arg2);
}
- NetworkScanCallback callback = nsi.mCallback;
- Executor executor = nsi.mExecutor;
- if (callback == null) {
- throw new RuntimeException(
- "Failed to find NetworkScanCallback with id " + message.arg2);
- }
- if (executor == null) {
- throw new RuntimeException(
- "Failed to find Executor with id " + message.arg2);
- }
+
+ final NetworkScanCallback callback = nsi.mCallback;
+ final Executor executor = nsi.mExecutor;
switch (message.what) {
case CALLBACK_RESTRICTED_SCAN_RESULTS:
@@ -246,17 +240,24 @@
NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
String callingPackage, @Nullable String callingFeatureId) {
try {
+ Objects.requireNonNull(request, "Request was null");
+ Objects.requireNonNull(callback, "Callback was null");
+ Objects.requireNonNull(executor, "Executor was null");
final ITelephony telephony = getITelephony();
if (telephony == null) return null;
- int scanId = telephony.requestNetworkScan(
- subId, request, mMessenger, new Binder(), callingPackage,
- callingFeatureId);
- if (scanId == INVALID_SCAN_ID) {
- Rlog.e(TAG, "Failed to initiate network scan");
- return null;
- }
+ // The lock must be taken before calling requestNetworkScan because the resulting
+ // scanId can be invoked asynchronously on another thread at any time after
+ // requestNetworkScan invoked, leaving a critical section between that call and adding
+ // the record to the ScanInfo cache.
synchronized (mScanInfo) {
+ int scanId = telephony.requestNetworkScan(
+ subId, request, mMessenger, new Binder(), callingPackage,
+ callingFeatureId);
+ if (scanId == INVALID_SCAN_ID) {
+ Rlog.e(TAG, "Failed to initiate network scan");
+ return null;
+ }
// We link to death whenever a scan is started to ensure that we are linked
// at the point that phone process death might matter.
// We never unlink because:
diff --git a/tests/DynamicCodeLoggerIntegrationTests/OWNERS b/tests/DynamicCodeLoggerIntegrationTests/OWNERS
new file mode 100644
index 0000000..d9eb141
--- /dev/null
+++ b/tests/DynamicCodeLoggerIntegrationTests/OWNERS
@@ -0,0 +1 @@
+file:/services/core/java/com/android/server/pm/dex/OWNERS
diff --git a/tests/SharedLibraryLoadingTest/Android.bp b/tests/SharedLibraryLoadingTest/Android.bp
new file mode 100644
index 0000000..088278d
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/Android.bp
@@ -0,0 +1,37 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_test_host {
+ name: "SharedLibraryLoadingTests",
+ libs: [
+ "tradefed",
+ "junit",
+ ],
+ test_suites: ["general-tests"],
+ data: [
+ ":SharedLibraryLoadingTests_StandardSharedLibrary",
+ ":SharedLibraryLoadingTests_SharedLibraryLoadedAfter",
+ ":SharedLibraryLoadingTests_SharedLibraryClientTests",
+ ":SharedLibraryLoadingTests_Overlay",
+ ],
+}
diff --git a/tests/SharedLibraryLoadingTest/AndroidTest.xml b/tests/SharedLibraryLoadingTest/AndroidTest.xml
new file mode 100644
index 0000000..947453d
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/AndroidTest.xml
@@ -0,0 +1,45 @@
+<?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.
+ -->
+
+<configuration description="Host-driven test module config for SharedLibraryHostTests">
+ <option name="test-tag" value="SharedLibraryLoadingTests" />
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="false" />
+ <option name="remount-system" value="true" />
+ <option name="push"
+ value="SharedLibraryLoadingTests_StandardSharedLibrary.apk->/product/app/SharedLibraryLoadingTests_StandardSharedLibrary.apk" />
+ <option name="push"
+ value="SharedLibraryLoadingTests_SharedLibraryLoadedAfter.apk->/product/app/SharedLibraryLoadingTests_SharedLibraryLoadedAfter.apk" />
+ <option name="push"
+ value="SharedLibraryLoadingTests_Overlay.apk->/product/overlay/SharedLibraryLoadingTests_Overlay.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="SharedLibraryLoadingTests_SharedLibraryClientTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.sharedlibloadingtest.client" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/OWNERS b/tests/SharedLibraryLoadingTest/OWNERS
new file mode 100644
index 0000000..d7b4569
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/OWNERS
@@ -0,0 +1,2 @@
+stenning@google.com
+
diff --git a/tests/SharedLibraryLoadingTest/test-apps/Overlay/Android.bp b/tests/SharedLibraryLoadingTest/test-apps/Overlay/Android.bp
new file mode 100644
index 0000000..b2f4e89
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/Overlay/Android.bp
@@ -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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "SharedLibraryLoadingTests_Overlay",
+ platform_apis: true,
+ certificate: "platform",
+ aaptflags: ["--no-resource-removal"],
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/Overlay/AndroidManifest.xml b/tests/SharedLibraryLoadingTest/test-apps/Overlay/AndroidManifest.xml
new file mode 100644
index 0000000..ae2784c
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/Overlay/AndroidManifest.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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.sharedlibloadingtest.overlay">
+ <application android:hasCode="false" />
+ <overlay android:targetPackage="android"
+ android:isStatic="true"
+ android:priority="1"/>
+</manifest>
\ No newline at end of file
diff --git a/core/java/android/companion/Association.aidl b/tests/SharedLibraryLoadingTest/test-apps/Overlay/res/values/config.xml
similarity index 60%
copy from core/java/android/companion/Association.aidl
copy to tests/SharedLibraryLoadingTest/test-apps/Overlay/res/values/config.xml
index 2a28f1f..15da3db 100644
--- a/core/java/android/companion/Association.aidl
+++ b/tests/SharedLibraryLoadingTest/test-apps/Overlay/res/values/config.xml
@@ -1,11 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,6 +15,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-
-parcelable Association;
+-->
+<resources>
+ <string-array name="config_sharedLibrariesLoadedAfterApp" translatable="false">
+ <item>com.android.sharedlibloadingtest.shared_library_after</item>
+ </string-array>
+</resources>
\ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/Android.bp b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/Android.bp
new file mode 100644
index 0000000..0d20497
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/Android.bp
@@ -0,0 +1,35 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "SharedLibraryLoadingTests_SharedLibraryClientTests",
+ srcs: ["**/*.java"],
+ resource_dirs: ["res"],
+ libs: [
+ "SharedLibraryLoadingTests_StandardSharedLibrary",
+ "SharedLibraryLoadingTests_SharedLibraryLoadedAfter",
+ "android.test.base",
+ ],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "androidx.test.core",
+ "testng",
+ ],
+ platform_apis: true,
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/AndroidManifest.xml b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/AndroidManifest.xml
new file mode 100644
index 0000000..e3a9b9b
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.sharedlibloadingtest.client">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <uses-library android:name="com.android.sharedlibloadingtest.shared_library"/>
+ <uses-library android:name="com.android.sharedlibloadingtest.shared_library_after"/>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.sharedlibloadingtest.client" />
+</manifest>
\ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/res/values/values.xml b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/res/values/values.xml
new file mode 100644
index 0000000..5e0544e
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/res/values/values.xml
@@ -0,0 +1,20 @@
+<?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>
+ <string name="identical_resource_key">client value</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/companion/Association.aidl b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/ClientClass.java
similarity index 72%
copy from core/java/android/companion/Association.aidl
copy to tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/ClientClass.java
index 2a28f1f..e48fb83 100644
--- a/core/java/android/companion/Association.aidl
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/ClientClass.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -13,6 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package com.android.sharedlibloadingtest;
+
+public class ClientClass {
+ @Override
+ public String toString() {
+ return "Client Code";
+ }
+}
diff --git a/core/java/android/companion/Association.aidl b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassA.java
similarity index 71%
copy from core/java/android/companion/Association.aidl
copy to tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassA.java
index 2a28f1f..4c77155 100644
--- a/core/java/android/companion/Association.aidl
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassA.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -13,6 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package com.android.sharedlibloadingtest;
+
+public class DuplicateClassA {
+ @Override
+ public String toString() {
+ return "Client's Version";
+ }
+}
diff --git a/core/java/android/companion/Association.aidl b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassB.java
similarity index 71%
copy from core/java/android/companion/Association.aidl
copy to tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassB.java
index 2a28f1f..86aa6a1 100644
--- a/core/java/android/companion/Association.aidl
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassB.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -13,6 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package com.android.sharedlibloadingtest;
+
+public class DuplicateClassB {
+ @Override
+ public String toString() {
+ return "Client's Version B";
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/client/SharedLibraryLoadingOrderTest.java b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/client/SharedLibraryLoadingOrderTest.java
new file mode 100644
index 0000000..43bcb1a
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/client/SharedLibraryLoadingOrderTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.sharedlibloadingtest.client;
+
+import static org.testng.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.util.Preconditions;
+import com.android.sharedlibloadingtest.ClientClass;
+import com.android.sharedlibloadingtest.DuplicateClassA;
+import com.android.sharedlibloadingtest.DuplicateClassB;
+import com.android.sharedlibloadingtest.SharedClassAfter;
+import com.android.sharedlibloadingtest.StdSharedClass;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.HashSet;
+
+@RunWith(AndroidJUnit4.class)
+public class SharedLibraryLoadingOrderTest {
+
+ @Test
+ public void testLoadingOfStdShareLibsShouldBeFirst() {
+ Preconditions.checkArgument(!getLibsLoadedAfter()
+ .contains("com.android.sharedlibloadingtest.shared_library"));
+ DuplicateClassA clazz = new DuplicateClassA();
+ assertEquals(clazz.toString(), "Standard Shared Lib's Version");
+
+ StdSharedClass stdSharedClass = new StdSharedClass();
+ assertEquals(stdSharedClass.toString(), "Nothing Special Lib");
+
+ ClientClass clientCode = new ClientClass();
+ assertEquals(clientCode.toString(), "Client Code");
+ }
+
+ @Test
+ public void testLoadingOfShareLibsIsAfter() {
+ Preconditions.checkArgument(getLibsLoadedAfter()
+ .contains("com.android.sharedlibloadingtest.shared_library_after"));
+ DuplicateClassB clazz = new DuplicateClassB();
+ assertEquals(clazz.toString(), "Client's Version B");
+
+ SharedClassAfter stdSharedClass = new SharedClassAfter();
+ assertEquals(stdSharedClass.toString(), "Also Nothing Special");
+
+ ClientClass clientCode = new ClientClass();
+ assertEquals(clientCode.toString(), "Client Code");
+ }
+
+ @Test
+ public void testLoadingOfResource() {
+ // aapt compiler gives each lib their own namespace so this test just confirming
+ // the resources can be loaded from the same context object
+ Context context = ApplicationProvider.getApplicationContext();
+ String clientString = context.getResources().getString(R.string.identical_resource_key);
+ assertEquals(clientString, "client value");
+ assertEquals(StdSharedClass.getResString(context), "std lib value");
+ assertEquals(SharedClassAfter.getResString(context), "loaded after value");
+
+ }
+
+ private HashSet<String> getLibsLoadedAfter() {
+ Resources systemR = Resources.getSystem();
+ HashSet<String> libsToLoadAfter = new HashSet<>();
+ Collections.addAll(libsToLoadAfter, systemR.getStringArray(
+ com.android.internal.R.array.config_sharedLibrariesLoadedAfterApp));
+ return libsToLoadAfter;
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/Android.bp b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/Android.bp
new file mode 100644
index 0000000..db9b3ed
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/Android.bp
@@ -0,0 +1,30 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "SharedLibraryLoadingTests_SharedLibraryLoadedAfter",
+ srcs: ["**/*.java"],
+ resource_dirs: ["res"],
+ sdk_version: "current",
+ aaptflags: ["--shared-lib"],
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/AndroidManifest.xml b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/AndroidManifest.xml
new file mode 100644
index 0000000..efedfcf
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/AndroidManifest.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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.sharedlibloadingtest.shared_library_after">
+ <application>
+ <library android:name="com.android.sharedlibloadingtest.shared_library_after" />
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/res/values/values.xml b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/res/values/values.xml
new file mode 100644
index 0000000..4525944
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/res/values/values.xml
@@ -0,0 +1,20 @@
+<?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>
+ <string name="identical_resource_key">loaded after value</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/companion/Association.aidl b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/DuplicateClassB.java
similarity index 70%
copy from core/java/android/companion/Association.aidl
copy to tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/DuplicateClassB.java
index 2a28f1f..1e1f5aa 100644
--- a/core/java/android/companion/Association.aidl
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/DuplicateClassB.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -13,6 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package com.android.sharedlibloadingtest;
+
+public class DuplicateClassB {
+ @Override
+ public String toString() {
+ return "Loaded After Shared Lib's Version";
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/SharedClassAfter.java b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/SharedClassAfter.java
new file mode 100644
index 0000000..9e5b40f
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/SharedClassAfter.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 com.android.sharedlibloadingtest;
+
+import android.content.Context;
+
+import com.android.sharedlibloadingtest.shared_library_after.R;
+
+public class SharedClassAfter {
+ @Override
+ public String toString() {
+ return "Also Nothing Special";
+ }
+
+ public static String getResString(Context context) {
+ return context.getResources().getString(R.string.identical_resource_key);
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/Android.bp b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/Android.bp
new file mode 100644
index 0000000..50456b0
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/Android.bp
@@ -0,0 +1,30 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "SharedLibraryLoadingTests_StandardSharedLibrary",
+ srcs: ["**/*.java"],
+ resource_dirs: ["res"],
+ sdk_version: "current",
+ aaptflags: ["--shared-lib"],
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/AndroidManifest.xml b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/AndroidManifest.xml
new file mode 100644
index 0000000..f1a079f
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/AndroidManifest.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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.sharedlibloadingtest.shared_library">
+ <application>
+ <library android:name="com.android.sharedlibloadingtest.shared_library" />
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/res/values/values.xml b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/res/values/values.xml
new file mode 100644
index 0000000..941351a
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/res/values/values.xml
@@ -0,0 +1,20 @@
+<?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>
+ <string name="identical_resource_key">std lib value</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/companion/Association.aidl b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/DuplicateClassA.java
similarity index 70%
copy from core/java/android/companion/Association.aidl
copy to tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/DuplicateClassA.java
index 2a28f1f..a3874aa 100644
--- a/core/java/android/companion/Association.aidl
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/DuplicateClassA.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -13,6 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package com.android.sharedlibloadingtest;
+
+public class DuplicateClassA {
+ @Override
+ public String toString() {
+ return "Standard Shared Lib's Version";
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/StdSharedClass.java b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/StdSharedClass.java
new file mode 100644
index 0000000..429d65c
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/StdSharedClass.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 com.android.sharedlibloadingtest;
+
+import android.content.Context;
+
+import com.android.sharedlibloadingtest.shared_library.R;
+
+public class StdSharedClass {
+ @Override
+ public String toString() {
+ return "Nothing Special Lib";
+ }
+
+ public static String getResString(Context context) {
+ return context.getResources().getString(R.string.identical_resource_key);
+ }
+}
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 72eaa35..11d02f0 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -120,6 +120,13 @@
static_cast<int>(parser.chunk()->type)));
}
}
+
+ if (!staged_entries_to_remove_.empty()) {
+ diag_->Error(DiagMessage(source_) << "didn't find " << staged_entries_to_remove_.size()
+ << " original staged resources");
+ return false;
+ }
+
return true;
}
@@ -393,6 +400,12 @@
return false;
}
+ if (const auto to_remove_it = staged_entries_to_remove_.find({name, res_id});
+ to_remove_it != staged_entries_to_remove_.end()) {
+ staged_entries_to_remove_.erase(to_remove_it);
+ continue;
+ }
+
NewResourceBuilder res_builder(name);
res_builder.SetValue(std::move(resource_value), config)
.SetId(res_id, OnIdConflict::CREATE_ENTRY)
@@ -533,9 +546,8 @@
// Since a the finalized resource entry is cloned and added to the resource table under the
// staged resource id, remove the cloned resource entry from the table.
if (!table_->RemoveResource(resource_name, staged_id)) {
- diag_->Error(DiagMessage(source_) << "failed to find resource entry for staged "
- << " resource ID " << staged_id);
- return false;
+ // If we haven't seen this resource yet let's add a record to skip it when parsing.
+ staged_entries_to_remove_.insert({resource_name, staged_id});
}
}
return true;
@@ -565,6 +577,8 @@
const ResTable_map_entry* map) {
switch (name.type) {
case ResourceType::kStyle:
+ // fallthrough
+ case ResourceType::kConfigVarying: // legacy thing used in tests
return ParseStyle(name, config, map);
case ResourceType::kAttrPrivate:
// fallthrough
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h
index cd71d16..1c83166 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -119,6 +119,10 @@
// A mapping of resource ID to type spec flags.
std::unordered_map<ResourceId, uint32_t> entry_type_spec_flags_;
+
+ // A collection of staged resources that got finalized already and we're supposed to prune -
+ // but the original staged resource record hasn't been parsed yet.
+ std::set<std::pair<ResourceName, ResourceId>> staged_entries_to_remove_;
};
} // namespace aapt