diff options
25 files changed, 484 insertions, 196 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java index b6361ce56569..80a70a6e9e9e 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java @@ -31,6 +31,7 @@ import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.expresslog.Counter; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateControllerProto; @@ -88,6 +89,8 @@ public final class TimeController extends StateController { // will never be unsatisfied (our time base can not go backwards). final long nowElapsedMillis = sElapsedRealtimeClock.millis(); if (job.hasDeadlineConstraint() && evaluateDeadlineConstraint(job, nowElapsedMillis)) { + // We're intentionally excluding jobs whose deadlines have passed + // (mostly like deadlines of 0) when the job was scheduled. return; } else if (job.hasTimingDelayConstraint() && evaluateTimingDelayConstraint(job, nowElapsedMillis)) { @@ -159,6 +162,8 @@ public final class TimeController extends StateController { // Scheduler. mStateChangedListener.onRunJobNow(job); } + Counter.logIncrement( + "job_scheduler.value_job_scheduler_job_deadline_expired_counter"); } else if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) { // This job's deadline is earlier than the current set alarm. Update the alarm. setDeadlineExpiredAlarmLocked(job.getLatestRunTimeElapsed(), @@ -229,6 +234,8 @@ public final class TimeController extends StateController { // Scheduler. mStateChangedListener.onRunJobNow(job); } + Counter.logIncrement( + "job_scheduler.value_job_scheduler_job_deadline_expired_counter"); it.remove(); } else { // Sorted by expiry time, so take the next one and stop. if (!wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) { diff --git a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java index 3b14be7327f7..24727c5f2448 100644 --- a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java +++ b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java @@ -107,7 +107,7 @@ public class DumpCommand extends Command { DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY); int rotation = display.getRotation(); Point size = new Point(); - display.getSize(size); + display.getRealSize(size); AccessibilityNodeInfoDumper.dumpWindowToFile(info, dumpFile, rotation, size.x, size.y); } diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java index ab198b319e27..488292d68620 100644 --- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java +++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java @@ -139,7 +139,7 @@ public class AccessibilityNodeInfoDumper { serializer.attribute("", "id", Integer.toString(displayId)); int rotation = display.getRotation(); Point size = new Point(); - display.getSize(size); + display.getRealSize(size); for (int i = 0, n = windows.size(); i < n; ++i) { dumpWindowRec(windows.get(i), serializer, i, size.x, size.y, rotation); } diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java index 6fd2bf250e2c..1bcd343e5668 100644 --- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java +++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java @@ -767,7 +767,7 @@ public class UiDevice { if(root != null) { Display display = getAutomatorBridge().getDefaultDisplay(); Point size = new Point(); - display.getSize(size); + display.getRealSize(size); AccessibilityNodeInfoDumper.dumpWindowToFile(root, new File(new File(Environment.getDataDirectory(), "local/tmp"), fileName), display.getRotation(), size.x, size.y); diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java index 3807b503ce70..2f32fa4216f1 100644 --- a/core/java/android/accounts/AbstractAccountAuthenticator.java +++ b/core/java/android/accounts/AbstractAccountAuthenticator.java @@ -120,27 +120,27 @@ public abstract class AbstractAccountAuthenticator { /** * Bundle key used for the {@link String} account type in session bundle. * This is used in the default implementation of - * {@link #startAddAccountSession} and {@link startUpdateCredentialsSession}. + * {@link #startAddAccountSession} and {@link #startUpdateCredentialsSession}. */ private static final String KEY_AUTH_TOKEN_TYPE = "android.accounts.AbstractAccountAuthenticato.KEY_AUTH_TOKEN_TYPE"; /** * Bundle key used for the {@link String} array of required features in * session bundle. This is used in the default implementation of - * {@link #startAddAccountSession} and {@link startUpdateCredentialsSession}. + * {@link #startAddAccountSession} and {@link #startUpdateCredentialsSession}. */ private static final String KEY_REQUIRED_FEATURES = "android.accounts.AbstractAccountAuthenticator.KEY_REQUIRED_FEATURES"; /** * Bundle key used for the {@link Bundle} options in session bundle. This is * used in default implementation of {@link #startAddAccountSession} and - * {@link startUpdateCredentialsSession}. + * {@link #startUpdateCredentialsSession}. */ private static final String KEY_OPTIONS = "android.accounts.AbstractAccountAuthenticator.KEY_OPTIONS"; /** * Bundle key used for the {@link Account} account in session bundle. This is used - * used in default implementation of {@link startUpdateCredentialsSession}. + * used in default implementation of {@link #startUpdateCredentialsSession}. */ private static final String KEY_ACCOUNT = "android.accounts.AbstractAccountAuthenticator.KEY_ACCOUNT"; diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 6d5c741e7dc7..3d10661062ea 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -911,11 +911,15 @@ public class Binder implements IBinder { final String transactionName = getTransactionName(transactionCode); final StringBuffer buf = new StringBuffer(); + // Keep trace name consistent with cpp trace name in: + // system/tools/aidl/generate_cpp.cpp + buf.append("AIDL::java::"); if (transactionName != null) { - buf.append(mSimpleDescriptor).append(":").append(transactionName); + buf.append(mSimpleDescriptor).append("::").append(transactionName); } else { - buf.append(mSimpleDescriptor).append("#").append(transactionCode); + buf.append(mSimpleDescriptor).append("::#").append(transactionCode); } + buf.append("::server"); transactionTraceName = buf.toString(); mTransactionTraceNames.setRelease(index, transactionTraceName); diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index c8c1fd4eba21..eb467e0dcc38 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -93,8 +93,9 @@ public class ApkSignatureSchemeV2Verifier { * associated with each signer. * * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2. - * @throws SecurityException if a APK Signature Scheme v2 signature of this APK does not verify. - * @throws IOException if an I/O error occurs while reading the APK file. + * @throws SecurityException if an APK Signature Scheme v2 signature of this APK does + * not verify. + * @throws IOException if an I/O error occurs while reading the APK file. */ public static X509Certificate[][] verify(String apkFile) throws SignatureNotFoundException, SecurityException, IOException { @@ -386,7 +387,6 @@ public class ApkSignatureSchemeV2Verifier { break; } } - return; } static byte[] getVerityRootHash(String apkPath) diff --git a/core/java/com/android/internal/expresslog/Counter.java b/core/java/com/android/internal/expresslog/Counter.java new file mode 100644 index 000000000000..7571073a9822 --- /dev/null +++ b/core/java/com/android/internal/expresslog/Counter.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.expresslog; + +import android.annotation.NonNull; + +import com.android.internal.util.FrameworkStatsLog; + +/** Counter encapsulates StatsD write API calls */ +public final class Counter { + + // Not instantiable. + private Counter() {} + + /** + * Increments Telemetry Express Counter metric by 1 + * @hide + */ + public static void logIncrement(@NonNull String metricId) { + logIncrement(metricId, 1); + } + + /** + * Increments Telemetry Express Counter metric by arbitrary value + * @hide + */ + public static void logIncrement(@NonNull String metricId, long amount) { + final long metricIdHash = hashString(metricId); + FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_EVENT_REPORTED, metricIdHash, amount); + } + + private static native long hashString(String stringToHash); +} diff --git a/core/jni/Android.bp b/core/jni/Android.bp index e032fa2d5547..7cd7d2957d25 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -34,6 +34,8 @@ cc_library_shared { "-Wno-error=deprecated-declarations", "-Wunused", "-Wunreachable-code", + + "-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash", ], cppflags: ["-Wno-conversion-null"], @@ -211,6 +213,7 @@ cc_library_shared { "android_content_res_Configuration.cpp", "android_security_Scrypt.cpp", "com_android_internal_content_om_OverlayConfig.cpp", + "com_android_internal_expresslog_Counter.cpp", "com_android_internal_net_NetworkUtilsInternal.cpp", "com_android_internal_os_ClassLoaderFactory.cpp", "com_android_internal_os_FuseAppLoop.cpp", @@ -244,6 +247,7 @@ cc_library_shared { "libscrypt_static", "libstatssocket_lazy", "libskia", + "libtextclassifier_hash_static", ], shared_libs: [ @@ -326,6 +330,7 @@ cc_library_shared { header_libs: [ "bionic_libc_platform_headers", "dnsproxyd_protocol_headers", + "libtextclassifier_hash_headers", ], }, host: { diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 6a051c39cab0..6b736488fe8b 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -193,6 +193,7 @@ extern int register_android_security_Scrypt(JNIEnv *env); extern int register_com_android_internal_content_F2fsUtils(JNIEnv* env); extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env); extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env); +extern int register_com_android_internal_expresslog_Counter(JNIEnv* env); extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env); extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env); extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env); @@ -1584,6 +1585,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_SharedMemory), REG_JNI(register_android_os_incremental_IncrementalManager), REG_JNI(register_com_android_internal_content_om_OverlayConfig), + REG_JNI(register_com_android_internal_expresslog_Counter), REG_JNI(register_com_android_internal_net_NetworkUtilsInternal), REG_JNI(register_com_android_internal_os_ClassLoaderFactory), REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter), diff --git a/core/jni/com_android_internal_expresslog_Counter.cpp b/core/jni/com_android_internal_expresslog_Counter.cpp new file mode 100644 index 000000000000..d4a8c23b8343 --- /dev/null +++ b/core/jni/com_android_internal_expresslog_Counter.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <nativehelper/JNIHelp.h> +#include <utils/hash/farmhash.h> + +#include "core_jni_helpers.h" + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +static jclass g_stringClass = nullptr; + +/** + * Class: com_android_internal_expresslog_Counter + * Method: hashString + * Signature: (Ljava/lang/String;)J + */ +static jlong hashString(JNIEnv* env, jclass /*class*/, jstring metricNameObj) { + ScopedUtfChars name(env, metricNameObj); + if (name.c_str() == nullptr) { + return 0; + } + + return static_cast<jlong>(farmhash::Fingerprint64(name.c_str(), name.size())); +} + +static const JNINativeMethod g_methods[] = { + {"hashString", "(Ljava/lang/String;)J", (void*)hashString}, +}; + +static const char* const kCounterPathName = "com/android/internal/expresslog/Counter"; + +namespace android { + +int register_com_android_internal_expresslog_Counter(JNIEnv* env) { + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + return RegisterMethodsOrDie(env, kCounterPathName, g_methods, NELEM(g_methods)); +} + +} // namespace android diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index cf1050461d68..343bc1cf11db 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -342,22 +342,6 @@ <!-- Mask to use when checking skb mark defined in config_networkWakeupPacketMark above. --> <integer name="config_networkWakeupPacketMask">0</integer> - <!-- Whether the APF Filter in the device should filter out IEEE 802.3 Frames - Those frames are identified by the field Eth-type having values - less than 0x600 --> - <bool translatable="false" name="config_apfDrop802_3Frames">true</bool> - - <!-- An array of Denylisted EtherType, packets with EtherTypes within this array - will be dropped - TODO: need to put proper values, these are for testing purposes only --> - <integer-array translatable="false" name="config_apfEthTypeBlackList"> - <item>0x88A2</item> - <item>0x88A4</item> - <item>0x88B8</item> - <item>0x88CD</item> - <item>0x88E3</item> - </integer-array> - <!-- Default value for ConnectivityManager.getMultipathPreference() on metered networks. Actual device behaviour is controlled by Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE. This is the default value of that setting. --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1f71bf9f95dd..7736c1ac2c2f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2016,8 +2016,6 @@ <java-symbol type="integer" name="config_networkAvoidBadWifi" /> <java-symbol type="integer" name="config_networkWakeupPacketMark" /> <java-symbol type="integer" name="config_networkWakeupPacketMask" /> - <java-symbol type="bool" name="config_apfDrop802_3Frames" /> - <java-symbol type="array" name="config_apfEthTypeBlackList" /> <java-symbol type="integer" name="config_networkDefaultDailyMultipathQuotaBytes" /> <java-symbol type="integer" name="config_networkMeteredMultipathPreference" /> <java-symbol type="array" name="config_networkSupportedKeepaliveCount" /> diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java index dbd918e35d70..62455988db34 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -30,6 +30,7 @@ import libcore.util.EmptyArray; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECParameterSpec; import java.security.spec.MGF1ParameterSpec; import java.util.Collection; import java.util.Locale; @@ -914,6 +915,51 @@ public abstract class KeyProperties { } /** + * @hide + */ + public abstract static class EcCurve { + private EcCurve() {} + + /** + * @hide + */ + public static int toKeymasterCurve(ECParameterSpec spec) { + int keySize = spec.getCurve().getField().getFieldSize(); + switch (keySize) { + case 224: + return android.hardware.security.keymint.EcCurve.P_224; + case 256: + return android.hardware.security.keymint.EcCurve.P_256; + case 384: + return android.hardware.security.keymint.EcCurve.P_384; + case 521: + return android.hardware.security.keymint.EcCurve.P_521; + default: + return -1; + } + } + + /** + * @hide + */ + public static int fromKeymasterCurve(int ecCurve) { + switch (ecCurve) { + case android.hardware.security.keymint.EcCurve.P_224: + return 224; + case android.hardware.security.keymint.EcCurve.P_256: + case android.hardware.security.keymint.EcCurve.CURVE_25519: + return 256; + case android.hardware.security.keymint.EcCurve.P_384: + return 384; + case android.hardware.security.keymint.EcCurve.P_521: + return 521; + default: + return -1; + } + } + } + + /** * Namespaces provide system developers and vendors with a way to use keystore without * requiring an applications uid. Namespaces can be configured using SEPolicy. * See <a href="https://source.android.com/security/keystore#access-control"> diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java index 5216a908826b..ace2053cc1a7 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java @@ -203,6 +203,11 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature for (Authorization a : key.getAuthorizations()) { if (a.keyParameter.tag == KeymasterDefs.KM_TAG_KEY_SIZE) { keySizeBits = KeyStore2ParameterUtils.getUnsignedInt(a); + break; + } else if (a.keyParameter.tag == KeymasterDefs.KM_TAG_EC_CURVE) { + keySizeBits = KeyProperties.EcCurve.fromKeymasterCurve( + a.keyParameter.value.getEcCurve()); + break; } } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 9d424e904d59..f05cdc57fb70 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -66,6 +66,7 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.security.interfaces.ECKey; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -566,6 +567,22 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { spec.getMaxUsageCount() )); } + if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(key.getAlgorithm())) { + if (key instanceof ECKey) { + ECKey ecKey = (ECKey) key; + importArgs.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_EC_CURVE, + KeyProperties.EcCurve.toKeymasterCurve(ecKey.getParams()) + )); + } + } + /* TODO: check for Ed25519(EdDSA) or X25519(XDH) key algorithm and + * add import args for KM_TAG_EC_CURVE as EcCurve.CURVE_25519. + * Currently conscrypt does not support EdDSA key import and XDH keys are not an + * instance of XECKey, hence these conditions are not added, once it is fully + * implemented by conscrypt, we can add CURVE_25519 argument for EdDSA and XDH + * algorithms. + */ } catch (IllegalArgumentException | IllegalStateException e) { throw new KeyStoreException(e); } diff --git a/packages/SettingsLib/src/com/android/settingslib/qrcode/QrDecorateView.java b/packages/SettingsLib/src/com/android/settingslib/qrcode/QrDecorateView.java index 51cf59c25502..ac9cdacec598 100644 --- a/packages/SettingsLib/src/com/android/settingslib/qrcode/QrDecorateView.java +++ b/packages/SettingsLib/src/com/android/settingslib/qrcode/QrDecorateView.java @@ -34,16 +34,16 @@ public class QrDecorateView extends View { private static final float CORNER_LINE_LENGTH = 264f; // 264dp private static final float CORNER_RADIUS = 16f; // 16dp - final private int mCornerColor; - final private int mFocusedCornerColor; - final private int mBackgroundColor; + private final int mCornerColor; + private final int mFocusedCornerColor; + private final int mBackgroundColor; - final private Paint mStrokePaint; - final private Paint mTransparentPaint; - final private Paint mBackgroundPaint; + private final Paint mStrokePaint; + private final Paint mTransparentPaint; + private final Paint mBackgroundPaint; - final private float mRadius; - final private float mInnerRidus; + private final float mRadius; + private final float mInnerRadius; private Bitmap mMaskBitmap; private Canvas mMaskCanvas; @@ -72,7 +72,7 @@ public class QrDecorateView extends View { mRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, getResources().getDisplayMetrics()); // Inner radius needs to minus stroke width for keeping the width of border consistent. - mInnerRidus = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + mInnerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS - CORNER_STROKE_WIDTH, getResources().getDisplayMetrics()); mCornerColor = context.getResources().getColor(R.color.qr_corner_line_color); @@ -95,7 +95,10 @@ public class QrDecorateView extends View { protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - if(mMaskBitmap == null) { + if (!isLaidOut()) { + return; + } + if (mMaskBitmap == null) { mMaskBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); mMaskCanvas = new Canvas(mMaskBitmap); } @@ -105,16 +108,18 @@ public class QrDecorateView extends View { @Override protected void onDraw(Canvas canvas) { - // Set frame line color. - mStrokePaint.setColor(mFocused ? mFocusedCornerColor : mCornerColor); - // Draw background color. - mMaskCanvas.drawColor(mBackgroundColor); - // Draw outer corner. - mMaskCanvas.drawRoundRect(mOuterFrame, mRadius, mRadius, mStrokePaint); - // Draw inner transparent corner. - mMaskCanvas.drawRoundRect(mInnerFrame, mInnerRidus, mInnerRidus, mTransparentPaint); - - canvas.drawBitmap(mMaskBitmap, 0, 0, mBackgroundPaint); + if (mMaskCanvas != null && mMaskBitmap != null) { + // Set frame line color. + mStrokePaint.setColor(mFocused ? mFocusedCornerColor : mCornerColor); + // Draw background color. + mMaskCanvas.drawColor(mBackgroundColor); + // Draw outer corner. + mMaskCanvas.drawRoundRect(mOuterFrame, mRadius, mRadius, mStrokePaint); + // Draw inner transparent corner. + mMaskCanvas.drawRoundRect(mInnerFrame, mInnerRadius, mInnerRadius, mTransparentPaint); + + canvas.drawBitmap(mMaskBitmap, 0, 0, mBackgroundPaint); + } super.onDraw(canvas); } diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java index 3f1d1feec282..ae50b2358139 100644 --- a/services/core/java/com/android/server/VpnManagerService.java +++ b/services/core/java/com/android/server/VpnManagerService.java @@ -186,6 +186,10 @@ public class VpnManagerService extends IVpnManager.Stub { synchronized (mVpns) { for (int i = 0; i < mVpns.size(); i++) { pw.println(mVpns.keyAt(i) + ": " + mVpns.valueAt(i).getPackage()); + pw.increaseIndent(); + mVpns.valueAt(i).dump(pw); + pw.decreaseIndent(); + pw.println(); } pw.decreaseIndent(); } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index c671a2c0cdad..0741d46b6f29 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -127,6 +127,8 @@ import android.system.keystore2.KeyDescriptor; import android.system.keystore2.KeyPermission; import android.text.TextUtils; import android.util.ArraySet; +import android.util.IndentingPrintWriter; +import android.util.LocalLog; import android.util.Log; import android.util.Range; @@ -296,6 +298,10 @@ public class Vpn { return mVpnProfileStore; } + private static final int MAX_EVENTS_LOGS = 20; + private final LocalLog mUnderlyNetworkChanges = new LocalLog(MAX_EVENTS_LOGS); + private final LocalLog mVpnManagerEvents = new LocalLog(MAX_EVENTS_LOGS); + /** * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This * only applies to {@link VpnService} connections. @@ -841,6 +847,9 @@ public class Vpn { int errorCode, @NonNull final String packageName, @Nullable final String sessionKey, @NonNull final VpnProfileState profileState, @Nullable final Network underlyingNetwork, @Nullable final NetworkCapabilities nc, @Nullable final LinkProperties lp) { + mVpnManagerEvents.log("Event class=" + getVpnManagerEventClassName(errorClass) + + ", err=" + getVpnManagerEventErrorName(errorCode) + " for " + packageName + + " on session " + sessionKey); final Intent intent = buildVpnManagerEventIntent(category, errorClass, errorCode, packageName, sessionKey, profileState, underlyingNetwork, nc, lp); return sendEventToVpnManagerApp(intent, packageName); @@ -1572,6 +1581,7 @@ public class Vpn { ? Arrays.asList(mConfig.underlyingNetworks) : null); mNetworkCapabilities = capsBuilder.build(); + logUnderlyNetworkChanges(mNetworkCapabilities.getUnderlyingNetworks()); mNetworkAgent = mDeps.newNetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */, mNetworkCapabilities, lp, new NetworkScore.Builder().setLegacyInt(VPN_DEFAULT_SCORE).build(), @@ -1599,6 +1609,11 @@ public class Vpn { } } + private void logUnderlyNetworkChanges(List<Network> networks) { + mUnderlyNetworkChanges.log("Switch to " + + ((networks != null) ? TextUtils.join(", ", networks) : "null")); + } + private void agentDisconnect(NetworkAgent networkAgent) { if (networkAgent != null) { networkAgent.unregister(); @@ -2928,7 +2943,6 @@ public class Vpn { ikeConfiguration.isIkeExtensionEnabled( IkeSessionConfiguration.EXTENSION_TYPE_MOBIKE); onIkeConnectionInfoChanged(token, ikeConfiguration.getIkeSessionConnectionInfo()); - mRetryCount = 0; } /** @@ -3048,6 +3062,7 @@ public class Vpn { } doSendLinkProperties(networkAgent, lp); + mRetryCount = 0; } catch (Exception e) { Log.d(TAG, "Error in ChildOpened for token " + token, e); onSessionLost(token, e); @@ -3345,6 +3360,10 @@ public class Vpn { } private void scheduleRetryNewIkeSession() { + if (mScheduledHandleRetryIkeSessionFuture != null) { + Log.d(TAG, "There is a pending retrying task, skip the new retrying task"); + return; + } final long retryDelay = mDeps.getNextRetryDelaySeconds(mRetryCount++); Log.d(TAG, "Retry new IKE session after " + retryDelay + " seconds."); // If the default network is lost during the retry delay, the mActiveNetwork will be @@ -4368,6 +4387,7 @@ public class Vpn { // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from // ConnectivityServiceTest. if (SdkLevel.isAtLeastT()) { + mVpnManagerEvents.log(packageName + " stopped"); sendEventToVpnManagerApp(intent, packageName); } } @@ -4535,8 +4555,10 @@ public class Vpn { /** Proxy to allow different testing setups */ // TODO: b/240492694 Remove VpnNetworkAgentWrapper and this method when // NetworkAgent#setUnderlyingNetworks can be un-finalized. - private static void doSetUnderlyingNetworks( + private void doSetUnderlyingNetworks( @NonNull NetworkAgent agent, @NonNull List<Network> networks) { + logUnderlyNetworkChanges(networks); + if (agent instanceof VpnNetworkAgentWrapper) { ((VpnNetworkAgentWrapper) agent).doSetUnderlyingNetworks(networks); } else { @@ -4655,4 +4677,57 @@ public class Vpn { static Range<Integer> createUidRangeForUser(int userId) { return new Range<Integer>(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1); } + + private String getVpnManagerEventClassName(int code) { + switch (code) { + case VpnManager.ERROR_CLASS_NOT_RECOVERABLE: + return "ERROR_CLASS_NOT_RECOVERABLE"; + case VpnManager.ERROR_CLASS_RECOVERABLE: + return "ERROR_CLASS_RECOVERABLE"; + default: + return "UNKNOWN_CLASS"; + } + } + + private String getVpnManagerEventErrorName(int code) { + switch (code) { + case VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST: + return "ERROR_CODE_NETWORK_UNKNOWN_HOST"; + case VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT: + return "ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT"; + case VpnManager.ERROR_CODE_NETWORK_IO: + return "ERROR_CODE_NETWORK_IO"; + case VpnManager.ERROR_CODE_NETWORK_LOST: + return "ERROR_CODE_NETWORK_LOST"; + default: + return "UNKNOWN_ERROR"; + } + } + + /** Dumps VPN state. */ + public void dump(IndentingPrintWriter pw) { + synchronized (Vpn.this) { + pw.println("Active package name: " + mPackage); + pw.println("Active vpn type: " + getActiveVpnType()); + pw.println("NetworkCapabilities: " + mNetworkCapabilities); + if (isIkev2VpnRunner()) { + final IkeV2VpnRunner runner = ((IkeV2VpnRunner) mVpnRunner); + pw.println("Token: " + runner.mSessionKey); + pw.println("MOBIKE " + (runner.mMobikeEnabled ? "enabled" : "disabled")); + if (mDataStallSuspected) pw.println("Data stall suspected"); + if (runner.mScheduledHandleDataStallFuture != null) { + pw.println("Reset session scheduled"); + } + } + pw.println("mUnderlyNetworkChanges (most recent first):"); + pw.increaseIndent(); + mUnderlyNetworkChanges.reverseDump(pw); + pw.decreaseIndent(); + + pw.println("mVpnManagerEvent (most recent first):"); + pw.increaseIndent(); + mVpnManagerEvents.reverseDump(pw); + pw.decreaseIndent(); + } + } } diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 5544669e1252..362b26ec762a 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -56,6 +56,7 @@ import android.content.pm.overlay.OverlayPaths; import android.content.res.ApkAssets; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Environment; import android.os.FabricatedOverlayInternal; import android.os.HandlerThread; @@ -876,7 +877,7 @@ public final class OverlayManagerService extends SystemService { } Slog.d(TAG, "commit failed: " + e.getMessage(), e); throw new SecurityException("commit failed" - + (DEBUG ? ": " + e.getMessage() : "")); + + (DEBUG || Build.IS_DEBUGGABLE ? ": " + e.getMessage() : "")); } } finally { traceEnd(TRACE_TAG_RRO); diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 46691a61930e..1e64701ebab7 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -3630,7 +3630,7 @@ class PackageManagerShellCommand extends ShellCommand { fd = ParcelFileDescriptor.dup(getInFileDescriptor()); } if (sizeBytes <= 0) { - getErrPrintWriter().println("Error: must specify a APK size"); + getErrPrintWriter().println("Error: must specify an APK size"); return 1; } diff --git a/services/core/java/com/android/server/pm/dex/OdsignStatsLogger.java b/services/core/java/com/android/server/pm/dex/OdsignStatsLogger.java index fa08addc9b69..227a3a1d4317 100644 --- a/services/core/java/com/android/server/pm/dex/OdsignStatsLogger.java +++ b/services/core/java/com/android/server/pm/dex/OdsignStatsLogger.java @@ -39,6 +39,7 @@ public class OdsignStatsLogger { // These need to be kept in sync with system/security/ondevice-signing/StatsReporter.{h, cpp}. private static final String METRICS_FILE = "/data/misc/odsign/metrics/odsign-metrics.txt"; private static final String COMPOS_METRIC_NAME = "comp_os_artifacts_check_record"; + private static final String ODSIGN_METRIC_NAME = "odsign_record"; /** * Arrange for stats to be uploaded in the background. @@ -64,18 +65,45 @@ public class OdsignStatsLogger { for (String line : lines.split("\n")) { String[] metrics = line.split(" "); - if (metrics.length != 4 || !metrics[0].equals(COMPOS_METRIC_NAME)) { - Slog.w(TAG, "Malformed metrics file"); - break; + if (line.isEmpty() || metrics.length < 1) { + Slog.w(TAG, "Empty metrics line"); + continue; } - boolean currentArtifactsOk = metrics[1].equals("1"); - boolean compOsPendingArtifactsExists = metrics[2].equals("1"); - boolean useCompOsGeneratedArtifacts = metrics[3].equals("1"); + switch (metrics[0]) { + case COMPOS_METRIC_NAME: { + if (metrics.length != 4) { + Slog.w(TAG, "Malformed CompOS metrics line '" + line + "'"); + continue; + } - ArtStatsLog.write(ArtStatsLog.EARLY_BOOT_COMP_OS_ARTIFACTS_CHECK_REPORTED, - currentArtifactsOk, compOsPendingArtifactsExists, - useCompOsGeneratedArtifacts); + boolean currentArtifactsOk = metrics[1].equals("1"); + boolean compOsPendingArtifactsExists = metrics[2].equals("1"); + boolean useCompOsGeneratedArtifacts = metrics[3].equals("1"); + + ArtStatsLog.write(ArtStatsLog.EARLY_BOOT_COMP_OS_ARTIFACTS_CHECK_REPORTED, + currentArtifactsOk, compOsPendingArtifactsExists, + useCompOsGeneratedArtifacts); + break; + } + case ODSIGN_METRIC_NAME: { + if (metrics.length != 2) { + Slog.w(TAG, "Malformed odsign metrics line '" + line + "'"); + continue; + } + + try { + int status = Integer.parseInt(metrics[1]); + ArtStatsLog.write(ArtStatsLog.ODSIGN_REPORTED, status); + } catch (NumberFormatException e) { + Slog.w(TAG, "Malformed odsign metrics line '" + line + "'"); + } + + break; + } + default: + Slog.w(TAG, "Malformed metrics line '" + line + "'"); + } } } catch (FileNotFoundException e) { // This is normal and probably means no new metrics have been generated. diff --git a/services/core/java/com/android/server/rollback/README.md b/services/core/java/com/android/server/rollback/README.md index 0c5cc156f86d..08800dada564 100644 --- a/services/core/java/com/android/server/rollback/README.md +++ b/services/core/java/com/android/server/rollback/README.md @@ -1,4 +1,4 @@ -#Rollback Manager +# Rollback Manager ## Introduction @@ -7,9 +7,9 @@ updatability efforts. RollbackManager adds support for rolling back an APK or APEX update to the previous version installed on the device, and reverting any APK or APEX data to the state it was in at the time of install. -##Rollback Basics +## Rollback Basics -###How Rollbacks Work +### How Rollbacks Work A new install parameter ENABLE_ROLLBACK can be specified to enable rollback when updating an application. For example: @@ -42,27 +42,27 @@ data taken when FooV2.apk was first installed. See below for more details of shell commands for rollback. -###Rollback Triggers +### Rollback Triggers -####Manually Triggered Rollback +#### Manually Triggered Rollback As mentioned above, it is possible to trigger rollback on device using a shell command. This is for testing purposes only. We do not expect this mechanism to be used in production in practice. -####Watchdog Triggered Rollback +#### Watchdog Triggered Rollback Watchdog triggered rollback is intended to address severe issues with the device. The platform provides several different watchdogs that can trigger rollback. -#####Package Watchdog +##### Package Watchdog There is a package watchdog service running on device that will trigger rollback of an update if there are 5 ANRs or process crashes within a 1 minute window for a package in the update. -#####Native Watchdog +##### Native Watchdog If a native service crashes repeatedly after an update is installed, rollback will be triggered. This particularly applies to updates that include APEXes @@ -70,25 +70,25 @@ that may update native services, but note that as it is difficult to tell which native services have been affected by an update, *any* crashing native service will cause the rollback to be triggered. -#####Explicit Health Check +##### Explicit Health Check There is an explicit check to verify the network stack is functional after an update. If there is no network connectivity within a certain time period after an update, rollback is triggered. -####Server Triggered Rollback +#### Server Triggered Rollback The RollbackManager API may be used by the installer to roll back an update based on a request from the server. -##Rollback Details +## Rollback Details -###RollbackManager API +### RollbackManager API The RollbackManager API is an @SystemAPI guarded by the MANAGE_ROLLBACKS and TEST_MANAGE_ROLLBACKS permissions. See RollbackManager.java for details about the RollbackManager API. -###Rollback of APEX modules +### Rollback of APEX modules Rollback is supported for APEX modules in addition to APK modules. In Q, there was no concept of data associated with an APEX, so only the APEX itself is @@ -100,7 +100,7 @@ terms of any state they persist on the system (outside of the APEX data directories). For example, FooV2.apex must not change the file format of some state stored on the device in such a way that FooV1.apex cannot read the file. -###Rollback of MultiPackage Installs +### Rollback of MultiPackage Installs Rollback can be enabled for multi-package installs. This requires that all packages in the install session, including the parent session, have the @@ -119,7 +119,7 @@ If there is a problem enabling rollback for any package in the multi-package install session, rollback will not be enabled for any package in the multi-package install session. -###Rollback of Staged Installs +### Rollback of Staged Installs Rollback can be enabled for staged installs, which require reboot to take effect. If reboot was required when the package was updated, then reboot is @@ -127,21 +127,21 @@ required when the package is rolled back. If no reboot was required when the package was updated, then no reboot is required when the package is rolled back. -###Rollbacks on Multi User Devices +### Rollbacks on Multi User Devices Rollbacks should work properly on devices with multiple users. There is special handling of user data backup to ensure app user data is properly backed up and restored for all users, even for credential encrypted users that have not been unlocked at various points during the flow. -###Rollback whitelist +### Rollback whitelist Outside of testing, rollback may only be enabled for packages listed in the sysconfig rollback whitelist - see `SystemConfig#getRollbackWhitelistedPackages`. Attempts to enable rollback for non-whitelisted packages will fail. -###Failure to Enable Rollback +### Failure to Enable Rollback There are a number of reasons why we may be unable to enable rollback for a package, including: @@ -158,13 +158,13 @@ If we are unable to enable rollback, the installation will proceed without rollback enabled. Failing to enable rollback does not cause the installation to fail. -###Failure to Commit Rollback +### Failure to Commit Rollback For the most part, a rollback will remain available after failure to commit it. This allows the caller to retry the rollback if they have reason to believe it will not fail again the next time the commit of the rollback is attempted. -###Installing Previously Rolled Back Packages +### Installing Previously Rolled Back Packages There is no logic in the platform itself to prevent installing a version of a package that was previously rolled back. @@ -175,7 +175,7 @@ package versions believed to be the main source of the bad update. The list of installer to prevent reinstall of a previously rolled back package version if so desired. -###Rollback Expiration +### Rollback Expiration An available rollback is expired if the rollback lifetime has been exceeded or if there is a new update to package associated with the rollback. When an @@ -183,9 +183,9 @@ available rollback is expired, the backed up apk and userdata associated with the rollback are deleted. Once a rollback is expired, it can no longer be executed. -##Shell Commands for Rollback +## Shell Commands for Rollback -###Installing an App with Rollback Enabled +### Installing an App with Rollback Enabled The `adb install` command accepts the `--enable-rollback` flag to install an app with rollback enabled. For example: @@ -194,7 +194,7 @@ with rollback enabled. For example: $ adb install --enable-rollback FooV2.apk ``` -###Triggering Rollback Manually +### Triggering Rollback Manually If rollback is available for an application, the pm command can be used to trigger rollback manually on device: @@ -206,7 +206,7 @@ $ adb shell pm rollback-app com.example.foo For rollback of staged installs, you have to manually reboot the device for the rollback to take effect after running the 'pm rollback-app' command. -###Listing the Status of Rollbacks on Device +### Listing the Status of Rollbacks on Device You can get a list with details about available and recently committed rollbacks using dumpsys. For example: @@ -246,9 +246,9 @@ committed. The list of rollbacks is also included in bug reports. Search for "DUMP OF SERVICE rollback". -##Configuration Properties +## Configuration Properties -###Rollback Lifetime +### Rollback Lifetime Rollback lifetime refers to the maximum duration of time after the rollback is first enabled that it will be available. The default is for rollbacks to be @@ -263,7 +263,7 @@ $ adb shell device_config put rollback_boot rollback_lifetime_in_millis 17280000 The update will not take effect until after system server has been restarted. -###Enable Rollback Timeout +### Enable Rollback Timeout The enable rollback timeout is how long RollbackManager is allowed to take to enable rollback when performing an update. This includes the time needed to make @@ -279,7 +279,7 @@ $ adb shell device_config put rollback enable_rollback_timeout 10000 The update will take effect for the next install with rollback enabled. -##Limitations +## Limitations * You cannot enable rollback for the first version of an application installed on the device. Only updates to a package previously installed on the device can diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 1ec191ed7c05..e14bbbd78ded 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -20,16 +20,15 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import android.app.ActivityManager.RunningTaskInfo; +import android.os.SystemClock; import android.os.UserHandle; import android.util.ArraySet; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledLambda; -import java.util.Comparator; -import java.util.Iterator; +import java.util.ArrayList; import java.util.List; -import java.util.TreeSet; /** * Class for resolving the set of running tasks in the system. @@ -41,15 +40,13 @@ class RunningTasks { static final int FLAG_CROSS_USERS = 1 << 2; static final int FLAG_KEEP_INTENT_EXTRA = 1 << 3; - // Comparator to sort by last active time (descending) - private static final Comparator<Task> LAST_ACTIVE_TIME_COMPARATOR = - (o1, o2) -> { - return o1.lastActiveTime == o2.lastActiveTime - ? Integer.signum(o2.mTaskId - o1.mTaskId) : - Long.signum(o2.lastActiveTime - o1.lastActiveTime); - }; - - private final TreeSet<Task> mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR); + // Tasks are sorted in order {focusedVisibleTasks, visibleTasks, invisibleTasks}. + private final ArrayList<Task> mTmpSortedTasks = new ArrayList<>(); + // mTmpVisibleTasks, mTmpInvisibleTasks and mTmpFocusedTasks are sorted from top + // to bottom. + private final ArrayList<Task> mTmpVisibleTasks = new ArrayList<>(); + private final ArrayList<Task> mTmpInvisibleTasks = new ArrayList<>(); + private final ArrayList<Task> mTmpFocusedTasks = new ArrayList<>(); private int mCallingUid; private int mUserId; @@ -67,8 +64,6 @@ class RunningTasks { return; } - // Gather all of the tasks across all of the tasks, and add them to the sorted set - mTmpSortedSet.clear(); mCallingUid = callingUid; mUserId = UserHandle.getUserId(callingUid); mCrossUser = (flags & FLAG_CROSS_USERS) == FLAG_CROSS_USERS; @@ -79,22 +74,67 @@ class RunningTasks { mRecentTasks = root.mService.getRecentTasks(); mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA; - final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this, - PooledLambda.__(Task.class)); - root.forAllLeafTasks(c, false); - c.recycle(); + if (root instanceof RootWindowContainer) { + ((RootWindowContainer) root).forAllDisplays(dc -> { + final Task focusedTask = dc.mFocusedApp != null ? dc.mFocusedApp.getTask() : null; + if (focusedTask != null) { + mTmpFocusedTasks.add(focusedTask); + } + processTaskInWindowContainer(dc); + }); + } else { + final DisplayContent dc = root.getDisplayContent(); + final Task focusedTask = dc != null + ? (dc.mFocusedApp != null ? dc.mFocusedApp.getTask() : null) + : null; + // May not be include focusedTask if root is DisplayArea. + final boolean rootContainsFocusedTask = focusedTask != null + && focusedTask.isDescendantOf(root); + if (rootContainsFocusedTask) { + mTmpFocusedTasks.add(focusedTask); + } + processTaskInWindowContainer(root); + } - // Take the first {@param maxNum} tasks and create running task infos for them - final Iterator<Task> iter = mTmpSortedSet.iterator(); - while (iter.hasNext()) { - if (maxNum == 0) { - break; + final int visibleTaskCount = mTmpVisibleTasks.size(); + for (int i = 0; i < mTmpFocusedTasks.size(); i++) { + final Task focusedTask = mTmpFocusedTasks.get(i); + final boolean containsFocusedTask = mTmpVisibleTasks.remove(focusedTask); + if (containsFocusedTask) { + // Put the visible focused task at the first position. + mTmpSortedTasks.add(focusedTask); } + } + if (!mTmpVisibleTasks.isEmpty()) { + mTmpSortedTasks.addAll(mTmpVisibleTasks); + } + if (!mTmpInvisibleTasks.isEmpty()) { + mTmpSortedTasks.addAll(mTmpInvisibleTasks); + } - final Task task = iter.next(); - list.add(createRunningTaskInfo(task)); - maxNum--; + // Take the first {@param maxNum} tasks and create running task infos for them + final int size = Math.min(maxNum, mTmpSortedTasks.size()); + final long now = SystemClock.elapsedRealtime(); + for (int i = 0; i < size; i++) { + final Task task = mTmpSortedTasks.get(i); + // Override the last active to current time for the visible tasks because the visible + // tasks can be considered to be currently active, the values are descending as + // the item order. + final long visibleActiveTime = i < visibleTaskCount ? now + size - i : -1; + list.add(createRunningTaskInfo(task, visibleActiveTime)); } + + mTmpFocusedTasks.clear(); + mTmpVisibleTasks.clear(); + mTmpInvisibleTasks.clear(); + mTmpSortedTasks.clear(); + } + + private void processTaskInWindowContainer(WindowContainer wc) { + final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this, + PooledLambda.__(Task.class)); + wc.forAllLeafTasks(c, true); + c.recycle(); } private void processTask(Task task) { @@ -121,25 +161,20 @@ class RunningTasks { // home & recent tasks return; } - if (task.isVisible()) { - // For the visible task, update the last active time so that it can be used to determine - // the order of the tasks (it may not be set for newly created tasks) - task.touchActiveTime(); - if (!task.isFocused()) { - // TreeSet doesn't allow the same value and make sure this task is lower than the - // focused one. - task.lastActiveTime -= mTmpSortedSet.size(); - } + mTmpVisibleTasks.add(task); + } else { + mTmpInvisibleTasks.add(task); } - - mTmpSortedSet.add(task); } /** Constructs a {@link RunningTaskInfo} from a given {@param task}. */ - private RunningTaskInfo createRunningTaskInfo(Task task) { + private RunningTaskInfo createRunningTaskInfo(Task task, long visibleActiveTime) { final RunningTaskInfo rti = new RunningTaskInfo(); task.fillTaskInfo(rti, !mKeepIntentExtra); + if (visibleActiveTime > 0) { + rti.lastActiveTime = visibleActiveTime; + } // Fill in some deprecated values rti.id = rti.taskId; return rti; diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java index 33b236669ec7..13fc61cbf2fa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java @@ -61,55 +61,6 @@ public class RunningTasksTest extends WindowTestsBase { } @Test - public void testCollectTasksByLastActiveTime() { - // Create a number of stacks with tasks (of incrementing active time) - final ArrayList<DisplayContent> displays = new ArrayList<>(); - final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build(); - displays.add(display); - - final int numStacks = 2; - for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) { - final Task stack = new TaskBuilder(mSupervisor) - .setDisplay(display) - .setOnTop(false) - .build(); - } - - final int numTasks = 10; - int activeTime = 0; - final List<Task> rootTasks = new ArrayList<>(); - display.getDefaultTaskDisplayArea().forAllRootTasks(task -> { - rootTasks.add(task); - }, false /* traverseTopToBottom */); - for (int i = 0; i < numTasks; i++) { - final Task task = - createTask(rootTasks.get(i % numStacks), ".Task" + i, i, activeTime++, null); - doReturn(false).when(task).isVisible(); - } - - // Ensure that the latest tasks were returned in order of decreasing last active time, - // collected from all tasks across all the stacks - final int numFetchTasks = 5; - ArrayList<RunningTaskInfo> tasks = new ArrayList<>(); - mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS, mRootWindowContainer, - -1 /* callingUid */, PROFILE_IDS); - assertThat(tasks).hasSize(numFetchTasks); - for (int i = 0; i < numFetchTasks; i++) { - assertEquals(numTasks - i - 1, tasks.get(i).id); - } - - // Ensure that requesting more than the total number of tasks only returns the subset - // and does not crash - tasks.clear(); - mRunningTasks.getTasks(100, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS, - mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS); - assertThat(tasks).hasSize(numTasks); - for (int i = 0; i < numTasks; i++) { - assertEquals(numTasks - i - 1, tasks.get(i).id); - } - } - - @Test public void testTaskInfo_expectNoExtrasByDefault() { final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build(); final int numTasks = 10; @@ -120,7 +71,7 @@ public class RunningTasksTest extends WindowTestsBase { .build(); final Bundle data = new Bundle(); data.putInt("key", 100); - createTask(stack, ".Task" + i, i, i, data); + createTask(stack, ".Task" + i, i, data); } final int numFetchTasks = 5; @@ -145,7 +96,7 @@ public class RunningTasksTest extends WindowTestsBase { .build(); final Bundle data = new Bundle(); data.putInt("key", 100); - createTask(stack, ".Task" + i, i, i, data); + createTask(stack, ".Task" + i, i, data); } final int numFetchTasks = 5; @@ -162,46 +113,63 @@ public class RunningTasksTest extends WindowTestsBase { } @Test - public void testUpdateLastActiveTimeOfVisibleTasks() { + public void testGetTasksSortByFocusAndVisibility() { final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build(); + final Task stack = new TaskBuilder(mSupervisor) + .setDisplay(display) + .setOnTop(true) + .build(); + final int numTasks = 10; final ArrayList<Task> tasks = new ArrayList<>(); for (int i = 0; i < numTasks; i++) { - final Task task = createTask(null, ".Task" + i, i, i, null); + final Task task = createTask(stack, ".Task" + i, i, null); doReturn(false).when(task).isVisible(); tasks.add(task); } - final Task visibleTask = tasks.get(0); - doReturn(true).when(visibleTask).isVisible(); - - final Task focusedTask = tasks.get(1); + final Task focusedTask = tasks.get(numTasks - 1); doReturn(true).when(focusedTask).isVisible(); - doReturn(true).when(focusedTask).isFocused(); + display.mFocusedApp = focusedTask.getTopNonFinishingActivity(); + + final Task visibleTaskTop = tasks.get(numTasks - 2); + doReturn(true).when(visibleTaskTop).isVisible(); - // Ensure that the last active time of visible tasks were updated while the focused one had - // the largest last active time. + final Task visibleTaskBottom = tasks.get(numTasks - 3); + doReturn(true).when(visibleTaskBottom).isVisible(); + + // Ensure that the focused Task is on top, visible tasks below, then invisible tasks. final int numFetchTasks = 5; final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>(); mRunningTasks.getTasks(numFetchTasks, fetchTasks, FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS); assertThat(fetchTasks).hasSize(numFetchTasks); - assertEquals(fetchTasks.get(0).id, focusedTask.mTaskId); - assertEquals(fetchTasks.get(1).id, visibleTask.mTaskId); + for (int i = 0; i < numFetchTasks; i++) { + assertEquals(numTasks - i - 1, fetchTasks.get(i).id); + } + + // Ensure that requesting more than the total number of tasks only returns the subset + // and does not crash + fetchTasks.clear(); + mRunningTasks.getTasks(100, fetchTasks, + FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer, + -1 /* callingUid */, PROFILE_IDS); + assertThat(fetchTasks).hasSize(numTasks); + for (int i = 0; i < numTasks; i++) { + assertEquals(numTasks - i - 1, fetchTasks.get(i).id); + } } /** - * Create a task with a single activity in it, with the given last active time. + * Create a task with a single activity in it. */ - private Task createTask(Task stack, String className, int taskId, - int lastActiveTime, Bundle extras) { + private Task createTask(Task stack, String className, int taskId, Bundle extras) { final Task task = new TaskBuilder(mAtm.mTaskSupervisor) .setComponent(new ComponentName(mContext.getPackageName(), className)) .setTaskId(taskId) .setParentTaskFragment(stack) .build(); - task.lastActiveTime = lastActiveTime; final ActivityRecord activity = new ActivityBuilder(mAtm) .setTask(task) .setComponent(new ComponentName(mContext.getPackageName(), ".TaskActivity")) |