diff options
32 files changed, 552 insertions, 292 deletions
diff --git a/Android.bp b/Android.bp index 2113e4b85248..5f02a15ad185 100644 --- a/Android.bp +++ b/Android.bp @@ -205,6 +205,7 @@ java_library { "apex_aidl_interface-java", "packagemanager_aidl-java", "framework-protos", + "libtombstone_proto_java", "updatable-driver-protos", "ota_metadata_proto_java", "android.hidl.base-V1.0-java", diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp index e7adf203334e..d03bbd249b00 100644 --- a/ProtoLibraries.bp +++ b/ProtoLibraries.bp @@ -34,7 +34,6 @@ gensrcs { ":ipconnectivity-proto-src", ":libstats_atom_enum_protos", ":libstats_atom_message_protos", - ":libtombstone_proto-src", "core/proto/**/*.proto", "libs/incident/**/*.proto", ], diff --git a/STABILITY_OWNERS b/STABILITY_OWNERS new file mode 100644 index 000000000000..a7ecb4dfdd44 --- /dev/null +++ b/STABILITY_OWNERS @@ -0,0 +1,2 @@ +gaillard@google.com + diff --git a/WEAR_OWNERS b/WEAR_OWNERS index 4f3bc27c380f..4127f996da03 100644 --- a/WEAR_OWNERS +++ b/WEAR_OWNERS @@ -4,3 +4,9 @@ yeabkal@google.com adsule@google.com andriyn@google.com yfz@google.com +con@google.com +leetodd@google.com +sadrul@google.com +rwmyers@google.com +nalmalki@google.com +shijianli@google.com diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp index 50c9fd3ec499..ef1fa6097056 100644 --- a/api/StubLibraries.bp +++ b/api/StubLibraries.bp @@ -912,7 +912,7 @@ droidstubs { } // This module can be built with: -// m out/soong/.intermediates/frameworks/base/api_versions_module_lib/android_common/metalava/api-versions.xml +// m out/soong/.intermediates/frameworks/base/api/api_versions_module_lib/android_common/metalava/api-versions.xml droidstubs { name: "api_versions_module_lib", srcs: [":android_module_stubs_current_with_test_libs{.jar}"], diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 4c2e4fc05949..8eca0fe4b775 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -10925,7 +10925,7 @@ package android.os { method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static int rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException; method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException; - method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException; + method @Deprecated public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException; field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME = 2000; // 0x7d0 field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED = 3000; // 0xbb8 field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE = 5000; // 0x1388 diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index f33d2991329a..4823f447f22b 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -1311,8 +1311,9 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim if (!node.mEnded) { float durationScale = ValueAnimator.getDurationScale(); durationScale = durationScale == 0 ? 1 : durationScale; - node.mEnded = node.mAnimation.pulseAnimationFrame( - (long) (animPlayTime * durationScale)); + if (node.mAnimation.pulseAnimationFrame((long) (animPlayTime * durationScale))) { + node.mEnded = true; + } } } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index a9b7257a5406..58717179d64d 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -1315,9 +1315,7 @@ public class Build { if (IS_ENG) return true; if (IS_TREBLE_ENABLED) { - // If we can run this code, the device should already pass AVB. - // So, we don't need to check AVB here. - int result = VintfObject.verifyWithoutAvb(); + int result = VintfObject.verifyBuildAtBoot(); if (result != 0) { Slog.e(TAG, "Vendor interface is incompatible, error=" diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl index 61b24aa55e30..b7649ba9700b 100644 --- a/core/java/android/os/ISystemConfig.aidl +++ b/core/java/android/os/ISystemConfig.aidl @@ -52,4 +52,9 @@ interface ISystemConfig { * @see SystemConfigManager#getDefaultVrComponents */ List<ComponentName> getDefaultVrComponents(); + + /** + * @see SystemConfigManager#getPreventUserDisablePackages + */ + List<String> getPreventUserDisablePackages(); } diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index 7f60a2097153..2145c1aa82c8 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -90,4 +90,8 @@ per-file CoolingDevice.java = file:/THERMAL_OWNERS per-file Temperature.java = file:/THERMAL_OWNERS # SecurityStateManager -per-file *SecurityStateManager* = file:/SECURITY_STATE_OWNERS
\ No newline at end of file +per-file *SecurityStateManager* = file:/SECURITY_STATE_OWNERS + +# SystemConfig +per-file ISystemConfig.aidl = file:/PACKAGE_MANAGER_OWNERS +per-file SystemConfigManager.java = file:/PACKAGE_MANAGER_OWNERS diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index a3b836adfc8b..d002fe1d6cc0 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -18,8 +18,6 @@ package android.os; import static android.view.Display.DEFAULT_DISPLAY; -import static java.nio.charset.StandardCharsets.UTF_8; - import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -47,11 +45,8 @@ import android.text.format.DateFormat; import android.util.Log; import android.view.Display; -import libcore.io.Streams; - import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; @@ -73,7 +68,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import java.util.zip.ZipInputStream; import sun.security.pkcs.PKCS7; import sun.security.pkcs.SignerInfo; @@ -423,72 +417,43 @@ public class RecoverySystem { } finally { raf.close(); } - - // Additionally verify the package compatibility. - if (!readAndVerifyPackageCompatibilityEntry(packageFile)) { - throw new SignatureException("package compatibility verification failed"); - } } /** * Verifies the compatibility entry from an {@link InputStream}. * - * @return the verification result. + * @param inputStream The stream that contains the package compatibility info. + * @throws IOException Never. + * @return {@code true}. + * @deprecated This function no longer checks {@code inputStream} and + * unconditionally returns true. Instead, check compatibility when the + * OTA package is generated. */ - @UnsupportedAppUsage + @Deprecated + @UnsupportedAppUsage( + publicAlternatives = "Use {@code true} directly", + maxTargetSdk = Build.VERSION_CODES.VANILLA_ICE_CREAM) private static boolean verifyPackageCompatibility(InputStream inputStream) throws IOException { - ArrayList<String> list = new ArrayList<>(); - ZipInputStream zis = new ZipInputStream(inputStream); - ZipEntry entry; - while ((entry = zis.getNextEntry()) != null) { - long entrySize = entry.getSize(); - if (entrySize > Integer.MAX_VALUE || entrySize < 0) { - throw new IOException( - "invalid entry size (" + entrySize + ") in the compatibility file"); - } - byte[] bytes = new byte[(int) entrySize]; - Streams.readFully(zis, bytes); - list.add(new String(bytes, UTF_8)); - } - if (list.isEmpty()) { - throw new IOException("no entries found in the compatibility file"); - } - return (VintfObject.verify(list.toArray(new String[list.size()])) == 0); - } - - /** - * Reads and verifies the compatibility entry in an OTA zip package. The compatibility entry is - * a zip file (inside the OTA package zip). - * - * @return {@code true} if the entry doesn't exist or verification passes. - */ - private static boolean readAndVerifyPackageCompatibilityEntry(File packageFile) - throws IOException { - try (ZipFile zip = new ZipFile(packageFile)) { - ZipEntry entry = zip.getEntry("compatibility.zip"); - if (entry == null) { - return true; - } - InputStream inputStream = zip.getInputStream(entry); - return verifyPackageCompatibility(inputStream); - } + return true; } /** * Verifies the package compatibility info against the current system. * * @param compatibilityFile the {@link File} that contains the package compatibility info. - * @throws IOException if there were any errors reading the compatibility file. - * @return the compatibility verification result. + * @throws IOException Never. + * @return {@code true} + * @deprecated This function no longer checks {@code compatibilityFile} and + * unconditionally returns true. Instead, check compatibility when the + * OTA package is generated. * * {@hide} */ + @Deprecated @SystemApi @SuppressLint("RequiresPermission") public static boolean verifyPackageCompatibility(File compatibilityFile) throws IOException { - try (InputStream inputStream = new FileInputStream(compatibilityFile)) { - return verifyPackageCompatibility(inputStream); - } + return true; } /** diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java index 77843d9fbb0a..21ffbf18dbc3 100644 --- a/core/java/android/os/SystemConfigManager.java +++ b/core/java/android/os/SystemConfigManager.java @@ -161,4 +161,18 @@ public class SystemConfigManager { } return Collections.emptyList(); } + + /** + * Return the packages that are prevented from being disabled, where if + * disabled it would result in a non-functioning system or similar. + * @hide + */ + @NonNull + public List<String> getPreventUserDisablePackages() { + try { + return mInterface.getPreventUserDisablePackages(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java index 1f11197afeee..4fc5131617b2 100644 --- a/core/java/android/os/VintfObject.java +++ b/core/java/android/os/VintfObject.java @@ -18,7 +18,6 @@ package android.os; import android.annotation.NonNull; import android.annotation.TestApi; -import android.util.Slog; import java.util.Map; @@ -44,44 +43,8 @@ public class VintfObject { public static native String[] report(); /** - * Verify that the given metadata for an OTA package is compatible with - * this device. - * - * @param packageInfo a list of serialized form of HalManifest's / - * CompatibilityMatri'ces (XML). - * @return = 0 if success (compatible) - * > 0 if incompatible - * < 0 if any error (mount partition fails, illformed XML, etc.) - * - * @deprecated Checking compatibility against an OTA package is no longer - * supported because the format of VINTF metadata in the OTA package may not - * be recognized by the current system. - * - * <p> - * <ul> - * <li>This function always returns 0 for non-empty {@code packageInfo}. - * </li> - * <li>This function returns the result of {@link #verifyWithoutAvb} for - * null or empty {@code packageInfo}.</li> - * </ul> - * - * @hide - */ - @Deprecated - public static int verify(String[] packageInfo) { - if (packageInfo != null && packageInfo.length > 0) { - Slog.w(LOG_TAG, "VintfObject.verify() with non-empty packageInfo is deprecated. " - + "Skipping compatibility checks for update package."); - return 0; - } - Slog.w(LOG_TAG, "VintfObject.verify() is deprecated. Call verifyWithoutAvb() instead."); - return verifyWithoutAvb(); - } - - /** - * Verify Vintf compatibility on the device without checking AVB - * (Android Verified Boot). It is useful to verify a running system - * image where AVB check is irrelevant. + * Verify Vintf compatibility on the device at boot time. Certain checks + * like kernel checks, AVB checks are disabled. * * @return = 0 if success (compatible) * > 0 if incompatible @@ -89,7 +52,7 @@ public class VintfObject { * * @hide */ - public static native int verifyWithoutAvb(); + public static native int verifyBuildAtBoot(); /** * @return a list of HAL names and versions that is supported by this diff --git a/core/java/com/android/server/OWNERS b/core/java/com/android/server/OWNERS deleted file mode 100644 index 1c2d19d94871..000000000000 --- a/core/java/com/android/server/OWNERS +++ /dev/null @@ -1 +0,0 @@ -per-file SystemConfig.java = file:/PACKAGE_MANAGER_OWNERS diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp index 1baea2aecc3c..b6517117ca62 100644 --- a/core/jni/android_os_VintfObject.cpp +++ b/core/jni/android_os_VintfObject.cpp @@ -46,6 +46,7 @@ using vintf::toXml; using vintf::Version; using vintf::VintfObject; using vintf::Vndk; +using vintf::CheckFlags::ENABLE_ALL_CHECKS; template<typename V> static inline jobjectArray toJavaStringArray(JNIEnv* env, const V& v) { @@ -93,12 +94,13 @@ static jobjectArray android_os_VintfObject_report(JNIEnv* env, jclass) return toJavaStringArray(env, cStrings); } -static jint android_os_VintfObject_verifyWithoutAvb(JNIEnv* env, jclass) { +static jint android_os_VintfObject_verifyBuildAtBoot(JNIEnv* env, jclass) { std::string error; - int32_t status = VintfObject::GetInstance()->checkCompatibility(&error, - ::android::vintf::CheckFlags::DISABLE_AVB_CHECK); + int32_t status = + VintfObject::GetInstance() + ->checkCompatibility(&error, ENABLE_ALL_CHECKS.disableAvb().disableKernel()); if (status) - LOG(WARNING) << "VintfObject.verifyWithoutAvb() returns " << status << ": " << error; + LOG(WARNING) << "VintfObject.verifyBuildAtBoot() returns " << status << ": " << error; return status; } @@ -170,7 +172,7 @@ static jobject android_os_VintfObject_getTargetFrameworkCompatibilityMatrixVersi static const JNINativeMethod gVintfObjectMethods[] = { {"report", "()[Ljava/lang/String;", (void*)android_os_VintfObject_report}, - {"verifyWithoutAvb", "()I", (void*)android_os_VintfObject_verifyWithoutAvb}, + {"verifyBuildAtBoot", "()I", (void*)android_os_VintfObject_verifyBuildAtBoot}, {"getHalNamesAndVersions", "()[Ljava/lang/String;", (void*)android_os_VintfObject_getHalNamesAndVersions}, {"getSepolicyVersion", "()Ljava/lang/String;", diff --git a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java index 43266a51502b..cb3f99c37a4f 100644 --- a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java +++ b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java @@ -17,6 +17,7 @@ package android.animation; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.util.PollingCheck; @@ -343,6 +344,20 @@ public class AnimatorSetCallsTest { } @Test + public void childAnimatorCancelsDuringUpdate_animatorSetIsEnded() throws Throwable { + mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + animation.cancel(); + } + }); + mActivity.runOnUiThread(() -> { + mSet1.start(); + assertFalse(mSet1.isRunning()); + }); + } + + @Test public void reentrantStart() throws Throwable { CountDownLatch latch = new CountDownLatch(3); AnimatorListenerAdapter listener = new AnimatorListenerAdapter() { diff --git a/data/keyboards/Android.bp b/data/keyboards/Android.bp new file mode 100644 index 000000000000..f15c1535a667 --- /dev/null +++ b/data/keyboards/Android.bp @@ -0,0 +1,29 @@ +// Copyright 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. + +genrule { + name: "validate_framework_keymaps", + srcs: [ + "*.kl", + "*.kcm", + "*.idc", + ], + tools: ["validatekeymaps"], + out: ["stamp"], + cmd: "$(location validatekeymaps) -q $(in) " + + "&& touch $(out)", + dist: { + targets: ["droidcore"], + }, +} diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk deleted file mode 100644 index 6ae88007f0f5..000000000000 --- a/data/keyboards/Android.mk +++ /dev/null @@ -1,44 +0,0 @@ -# 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. - -# This makefile performs build time validation of framework keymap files. - -LOCAL_PATH := $(call my-dir) - -include $(LOCAL_PATH)/common.mk - -# Validate all key maps. -include $(CLEAR_VARS) - -LOCAL_MODULE := validate_framework_keymaps -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE -intermediates := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE),,COMMON) -LOCAL_BUILT_MODULE := $(intermediates)/stamp - -validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX) -$(LOCAL_BUILT_MODULE): PRIVATE_VALIDATEKEYMAPS := $(validatekeymaps) -$(LOCAL_BUILT_MODULE) : $(framework_keylayouts) $(framework_keycharmaps) $(framework_keyconfigs) | $(validatekeymaps) - $(hide) $(PRIVATE_VALIDATEKEYMAPS) -q $^ - $(hide) mkdir -p $(dir $@) && touch $@ - -# Run validatekeymaps uncondionally for platform build. -droidcore : $(LOCAL_BUILT_MODULE) - -# Reset temp vars. -validatekeymaps := -framework_keylayouts := -framework_keycharmaps := -framework_keyconfigs := diff --git a/data/keyboards/common.mk b/data/keyboards/common.mk deleted file mode 100644 index d75b691bd585..000000000000 --- a/data/keyboards/common.mk +++ /dev/null @@ -1,22 +0,0 @@ -# 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. - -# This is the list of framework provided keylayouts and key character maps to include. -# Used by Android.mk and keyboards.mk. - -framework_keylayouts := $(wildcard $(LOCAL_PATH)/*.kl) - -framework_keycharmaps := $(wildcard $(LOCAL_PATH)/*.kcm) - -framework_keyconfigs := $(wildcard $(LOCAL_PATH)/*.idc) diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java index 926d7a4d3ea6..5cdfca7392e3 100644 --- a/services/core/java/com/android/server/BootReceiver.java +++ b/services/core/java/com/android/server/BootReceiver.java @@ -48,6 +48,8 @@ import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import com.android.server.am.DropboxRateLimiter; +import com.android.server.os.TombstoneProtos; +import com.android.server.os.TombstoneProtos.Tombstone; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -60,11 +62,14 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.attribute.PosixFilePermissions; +import java.util.AbstractMap; import java.util.HashMap; import java.util.Iterator; +import java.util.Map; import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * Performs a number of miscellaneous, non-system-critical actions @@ -327,12 +332,12 @@ public class BootReceiver extends BroadcastReceiver { * * @param ctx Context * @param tombstone path to the tombstone - * @param proto whether the tombstone is stored as proto + * @param tombstoneProto the parsed proto tombstone * @param processName the name of the process corresponding to the tombstone * @param tmpFileLock the lock for reading/writing tmp files */ public static void addTombstoneToDropBox( - Context ctx, File tombstone, boolean proto, String processName, + Context ctx, File tombstone, Tombstone tombstoneProto, String processName, ReentrantLock tmpFileLock) { final DropBoxManager db = ctx.getSystemService(DropBoxManager.class); if (db == null) { @@ -342,31 +347,33 @@ public class BootReceiver extends BroadcastReceiver { // Check if we should rate limit and abort early if needed. DropboxRateLimiter.RateLimitResult rateLimitResult = - sDropboxRateLimiter.shouldRateLimit( - proto ? TAG_TOMBSTONE_PROTO_WITH_HEADERS : TAG_TOMBSTONE, processName); + sDropboxRateLimiter.shouldRateLimit(TAG_TOMBSTONE_PROTO_WITH_HEADERS, processName); if (rateLimitResult.shouldRateLimit()) return; HashMap<String, Long> timestamps = readTimestamps(); try { - if (proto) { - if (recordFileTimestamp(tombstone, timestamps)) { - // We need to attach the count indicating the number of dropped dropbox entries - // due to rate limiting. Do this by enclosing the proto tombsstone in a - // container proto that has the dropped entry count and the proto tombstone as - // bytes (to avoid the complexity of reading and writing nested protos). - tmpFileLock.lock(); - try { - addAugmentedProtoToDropbox(tombstone, db, rateLimitResult); - } finally { - tmpFileLock.unlock(); - } + // Remove the memory data from the proto. + Tombstone tombstoneProtoWithoutMemory = removeMemoryFromTombstone(tombstoneProto); + + final byte[] tombstoneBytes = tombstoneProtoWithoutMemory.toByteArray(); + + // Use JNI to call the c++ proto to text converter and add the headers to the tombstone. + String tombstoneWithoutMemory = new StringBuilder(getBootHeadersToLogAndUpdate()) + .append(rateLimitResult.createHeader()) + .append(getTombstoneText(tombstoneBytes)) + .toString(); + + // Add the tombstone without memory data to dropbox. + db.addText(TAG_TOMBSTONE, tombstoneWithoutMemory); + + // Add the tombstone proto to dropbox. + if (recordFileTimestamp(tombstone, timestamps)) { + tmpFileLock.lock(); + try { + addAugmentedProtoToDropbox(tombstone, tombstoneBytes, db, rateLimitResult); + } finally { + tmpFileLock.unlock(); } - } else { - // Add the header indicating how many events have been dropped due to rate limiting. - final String headers = getBootHeadersToLogAndUpdate() - + rateLimitResult.createHeader(); - addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE, - TAG_TOMBSTONE); } } catch (IOException e) { Slog.e(TAG, "Can't log tombstone", e); @@ -375,11 +382,8 @@ public class BootReceiver extends BroadcastReceiver { } private static void addAugmentedProtoToDropbox( - File tombstone, DropBoxManager db, + File tombstone, byte[] tombstoneBytes, DropBoxManager db, DropboxRateLimiter.RateLimitResult rateLimitResult) throws IOException { - // Read the proto tombstone file as bytes. - final byte[] tombstoneBytes = Files.readAllBytes(tombstone.toPath()); - final File tombstoneProtoWithHeaders = File.createTempFile( tombstone.getName(), ".tmp", TOMBSTONE_TMP_DIR); Files.setPosixFilePermissions( @@ -412,6 +416,8 @@ public class BootReceiver extends BroadcastReceiver { } } + private static native String getTombstoneText(byte[] tombstoneBytes); + private static void addLastkToDropBox( DropBoxManager db, HashMap<String, Long> timestamps, String headers, String footers, String filename, int maxSize, @@ -429,6 +435,31 @@ public class BootReceiver extends BroadcastReceiver { addFileWithFootersToDropBox(db, timestamps, headers, footers, filename, maxSize, tag); } + /** Removes memory information from the Tombstone proto. */ + @VisibleForTesting + public static Tombstone removeMemoryFromTombstone(Tombstone tombstoneProto) { + Tombstone.Builder tombstoneBuilder = tombstoneProto.toBuilder() + .clearMemoryMappings() + .clearThreads() + .putAllThreads(tombstoneProto.getThreadsMap().entrySet() + .stream() + .map(BootReceiver::clearMemoryDump) + .collect(Collectors.toMap(e->e.getKey(), e->e.getValue()))); + + if (tombstoneProto.hasSignalInfo()) { + tombstoneBuilder.setSignalInfo( + tombstoneProto.getSignalInfo().toBuilder().clearFaultAdjacentMetadata()); + } + + return tombstoneBuilder.build(); + } + + private static AbstractMap.SimpleEntry<Integer, TombstoneProtos.Thread> clearMemoryDump( + Map.Entry<Integer, TombstoneProtos.Thread> e) { + return new AbstractMap.SimpleEntry<Integer, TombstoneProtos.Thread>( + e.getKey(), e.getValue().toBuilder().clearMemoryDump().build()); + } + private static void addFileToDropBox( DropBoxManager db, HashMap<String, Long> timestamps, String headers, String filename, int maxSize, String tag) throws IOException { diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index a2d7a81b3289..5335cc310af1 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -43,3 +43,6 @@ per-file SystemTimeZone.java = file:/services/core/java/com/android/server/timez per-file TelephonyRegistry.java = file:/telephony/OWNERS per-file UiModeManagerService.java = file:/packages/SystemUI/OWNERS per-file VcnManagementService.java = file:/services/core/java/com/android/server/vcn/OWNERS + +# SystemConfig +per-file SystemConfig.java = file:/PACKAGE_MANAGER_OWNERS diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index bca2d60761d1..b04c7c52efbd 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -316,6 +316,11 @@ public class SystemConfig { private final ArraySet<String> mBugreportWhitelistedPackages = new ArraySet<>(); private final ArraySet<String> mAppDataIsolationWhitelistedApps = new ArraySet<>(); + // These packages will be set as 'prevent disable', where they are no longer possible + // for the end user to disable via settings. This flag should only be used for packages + // which meet the 'force or keep enabled apps' policy. + private final ArrayList<String> mPreventUserDisablePackages = new ArrayList<>(); + // Map of packagesNames to userTypes. Stored temporarily until cleared by UserManagerService(). private ArrayMap<String, Set<String>> mPackageToUserTypeWhitelist = new ArrayMap<>(); private ArrayMap<String, Set<String>> mPackageToUserTypeBlacklist = new ArrayMap<>(); @@ -501,6 +506,10 @@ public class SystemConfig { return mAppDataIsolationWhitelistedApps; } + public @NonNull ArrayList<String> getPreventUserDisablePackages() { + return mPreventUserDisablePackages; + } + /** * Gets map of packagesNames to userTypes, dictating on which user types each package should be * initially installed, and then removes this map from SystemConfig. @@ -1303,6 +1312,16 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; + case "prevent-disable": { + String pkgname = parser.getAttributeValue(null, "package"); + if (pkgname == null) { + Slog.w(TAG, "<" + name + "> without package in " + permFile + + " at " + parser.getPositionDescription()); + } else { + mPreventUserDisablePackages.add(pkgname); + } + XmlUtils.skipCurrentTag(parser); + } break; case "install-in-user-type": { // NB: We allow any directory permission to declare install-in-user-type. readInstallInUserType(parser, diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java index ab0d0d2626db..b7e737448d2d 100644 --- a/services/core/java/com/android/server/os/NativeTombstoneManager.java +++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java @@ -41,14 +41,13 @@ import android.system.Os; import android.system.StructStat; import android.util.Slog; import android.util.SparseArray; -import android.util.proto.ProtoInputStream; -import android.util.proto.ProtoParseException; import com.android.internal.annotations.GuardedBy; import com.android.server.BootReceiver; import com.android.server.ServiceThread; import com.android.server.os.TombstoneProtos.Cause; import com.android.server.os.TombstoneProtos.Tombstone; +import com.android.server.os.protobuf.CodedInputStream; import libcore.io.IoUtils; @@ -128,18 +127,21 @@ public final class NativeTombstoneManager { return; } - String processName = "UNKNOWN"; final boolean isProtoFile = filename.endsWith(".pb"); - File protoPath = isProtoFile ? path : new File(path.getAbsolutePath() + ".pb"); + if (!isProtoFile) { + return; + } - Optional<TombstoneFile> parsedTombstone = handleProtoTombstone(protoPath, isProtoFile); + Optional<ParsedTombstone> parsedTombstone = handleProtoTombstone(path, true); if (parsedTombstone.isPresent()) { - processName = parsedTombstone.get().getProcessName(); + BootReceiver.addTombstoneToDropBox( + mContext, path, parsedTombstone.get().getTombstone(), + parsedTombstone.get().getProcessName(), mTmpFileLock); } - BootReceiver.addTombstoneToDropBox(mContext, path, isProtoFile, processName, mTmpFileLock); } - private Optional<TombstoneFile> handleProtoTombstone(File path, boolean addToList) { + private Optional<ParsedTombstone> handleProtoTombstone( + File path, boolean addToList) { final String filename = path.getName(); if (!filename.endsWith(".pb")) { Slog.w(TAG, "unexpected tombstone name: " + path); @@ -169,7 +171,7 @@ public final class NativeTombstoneManager { return Optional.empty(); } - final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd); + final Optional<ParsedTombstone> parsedTombstone = TombstoneFile.parse(pfd); if (!parsedTombstone.isPresent()) { IoUtils.closeQuietly(pfd); return Optional.empty(); @@ -182,7 +184,7 @@ public final class NativeTombstoneManager { previous.dispose(); } - mTombstones.put(number, parsedTombstone.get()); + mTombstones.put(number, parsedTombstone.get().getTombstoneFile()); } } @@ -330,6 +332,27 @@ public final class NativeTombstoneManager { } } + static class ParsedTombstone { + TombstoneFile mTombstoneFile; + Tombstone mTombstone; + ParsedTombstone(TombstoneFile tombstoneFile, Tombstone tombstone) { + mTombstoneFile = tombstoneFile; + mTombstone = tombstone; + } + + public String getProcessName() { + return mTombstoneFile.getProcessName(); + } + + public TombstoneFile getTombstoneFile() { + return mTombstoneFile; + } + + public Tombstone getTombstone() { + return mTombstone; + } + } + static class TombstoneFile { final ParcelFileDescriptor mPfd; @@ -412,67 +435,21 @@ public final class NativeTombstoneManager { } } - static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) { - final FileInputStream is = new FileInputStream(pfd.getFileDescriptor()); - final ProtoInputStream stream = new ProtoInputStream(is); - - int pid = 0; - int uid = 0; - String processName = null; - String crashReason = ""; - String selinuxLabel = ""; - - try { - while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { - switch (stream.getFieldNumber()) { - case (int) Tombstone.PID: - pid = stream.readInt(Tombstone.PID); - break; - - case (int) Tombstone.UID: - uid = stream.readInt(Tombstone.UID); - break; - - case (int) Tombstone.COMMAND_LINE: - if (processName == null) { - processName = stream.readString(Tombstone.COMMAND_LINE); - } - break; + static Optional<ParsedTombstone> parse(ParcelFileDescriptor pfd) { + Tombstone tombstoneProto; + try (FileInputStream is = new FileInputStream(pfd.getFileDescriptor())) { + final byte[] tombstoneBytes = is.readAllBytes(); - case (int) Tombstone.CAUSES: - if (!crashReason.equals("")) { - // Causes appear in decreasing order of likelihood. For now we only - // want the most likely crash reason here, so ignore all others. - break; - } - long token = stream.start(Tombstone.CAUSES); - cause: - while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { - switch (stream.getFieldNumber()) { - case (int) Cause.HUMAN_READABLE: - crashReason = stream.readString(Cause.HUMAN_READABLE); - break cause; - - default: - break; - } - } - stream.end(token); - break; - - case (int) Tombstone.SELINUX_LABEL: - selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL); - break; - - default: - break; - } - } - } catch (IOException | ProtoParseException ex) { + tombstoneProto = Tombstone.parseFrom( + CodedInputStream.newInstance(tombstoneBytes)); + } catch (IOException ex) { Slog.e(TAG, "Failed to parse tombstone", ex); return Optional.empty(); } + int pid = tombstoneProto.getPid(); + int uid = tombstoneProto.getUid(); + if (!UserHandle.isApp(uid)) { Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring"); return Optional.empty(); @@ -489,6 +466,7 @@ public final class NativeTombstoneManager { final int userId = UserHandle.getUserId(uid); final int appId = UserHandle.getAppId(uid); + String selinuxLabel = tombstoneProto.getSelinuxLabel(); if (!selinuxLabel.startsWith("u:r:untrusted_app")) { Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring"); return Optional.empty(); @@ -500,11 +478,30 @@ public final class NativeTombstoneManager { result.mAppId = appId; result.mPid = pid; result.mUid = uid; - result.mProcessName = processName == null ? "" : processName; + result.mProcessName = getCmdLineProcessName(tombstoneProto); result.mTimestampMs = timestampMs; - result.mCrashReason = crashReason; + result.mCrashReason = getCrashReason(tombstoneProto); - return Optional.of(result); + return Optional.of(new ParsedTombstone(result, tombstoneProto)); + } + + private static String getCmdLineProcessName(Tombstone tombstoneProto) { + for (String cmdline : tombstoneProto.getCommandLineList()) { + if (cmdline != null) { + return cmdline; + } + } + return ""; + } + + private static String getCrashReason(Tombstone tombstoneProto) { + for (Cause cause : tombstoneProto.getCausesList()) { + if (cause.getHumanReadable() != null + && !cause.getHumanReadable().equals("")) { + return cause.getHumanReadable(); + } + } + return ""; } public IParcelFileDescriptorRetriever getPfdRetriever() { diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index d2d2a0cba085..ed9445c26740 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -1442,6 +1442,13 @@ public class TrustManagerService extends SystemService { if (biometricManager == null) { return new long[0]; } + if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2() + && mLockPatternUtils.isProfileWithUnifiedChallenge(userId)) { + // Profiles with unified challenge have their own set of biometrics, but the device + // unlock happens via the parent user. In this case Keystore needs to be given the list + // of biometric SIDs from the parent user, not the profile. + userId = resolveProfileParent(userId); + } return biometricManager.getAuthenticatorIds(userId); } @@ -1933,7 +1940,8 @@ public class TrustManagerService extends SystemService { }; } - private final PackageMonitor mPackageMonitor = new PackageMonitor() { + @VisibleForTesting + final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override public void onSomePackagesChanged() { refreshAgentList(UserHandle.USER_ALL); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index e51afbe20717..0bc60cd8efb5 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4708,6 +4708,7 @@ class Task extends TaskFragment { } if (top.isAttached()) { top.setWindowingMode(WINDOWING_MODE_UNDEFINED); + top.mWaitForEnteringPinnedMode = false; } } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 8cd55c7dd506..591a55972b81 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -37,6 +37,7 @@ cc_library_static { "com_android_server_adb_AdbDebuggingManager.cpp", "com_android_server_am_BatteryStatsService.cpp", "com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp", + "com_android_server_BootReceiver.cpp", "com_android_server_ConsumerIrService.cpp", "com_android_server_companion_virtual_InputController.cpp", "com_android_server_devicepolicy_CryptoTestHelper.cpp", @@ -91,6 +92,16 @@ cc_library_static { header_libs: [ "bionic_libc_platform_headers", ], + + static_libs: [ + "libunwindstack", + ], + + whole_static_libs: [ + "libdebuggerd_tombstone_proto_to_text", + ], + + runtime_libs: ["libdexfile"], } cc_defaults { diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS index d4f6312d19d9..33d3686ee9ce 100644 --- a/services/core/jni/OWNERS +++ b/services/core/jni/OWNERS @@ -32,3 +32,7 @@ per-file com_android_server_companion_virtual_InputController.cpp = file:/servic # Bug component : 158088 = per-file *AnrTimer* per-file *AnrTimer* = file:/PERFORMANCE_OWNERS + +# Bug component : 158088 = per-file com_android_server_utils_AnrTimer*.java +per-file com_android_server_utils_AnrTimer*.java = file:/PERFORMANCE_OWNERS +per-file com_android_server_BootReceiver.cpp = file:/STABILITY_OWNERS diff --git a/services/core/jni/com_android_server_BootReceiver.cpp b/services/core/jni/com_android_server_BootReceiver.cpp new file mode 100644 index 000000000000..3892d284dafb --- /dev/null +++ b/services/core/jni/com_android_server_BootReceiver.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <libdebuggerd/tombstone.h> +#include <nativehelper/JNIHelp.h> + +#include <sstream> + +#include "jni.h" +#include "tombstone.pb.h" + +namespace android { + +static void writeToString(std::stringstream& ss, const std::string& line, bool should_log) { + ss << line << std::endl; +} + +static jstring com_android_server_BootReceiver_getTombstoneText(JNIEnv* env, jobject, + jbyteArray tombstoneBytes) { + Tombstone tombstone; + tombstone.ParseFromArray(env->GetByteArrayElements(tombstoneBytes, 0), + env->GetArrayLength(tombstoneBytes)); + + std::stringstream tombstoneString; + + tombstone_proto_to_text(tombstone, + std::bind(&writeToString, std::ref(tombstoneString), + std::placeholders::_1, std::placeholders::_2)); + + return env->NewStringUTF(tombstoneString.str().c_str()); +} + +static const JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"getTombstoneText", "([B)Ljava/lang/String;", + (jstring*)com_android_server_BootReceiver_getTombstoneText}, +}; + +int register_com_android_server_BootReceiver(JNIEnv* env) { + return jniRegisterNativeMethods(env, "com/android/server/BootReceiver", sMethods, + NELEM(sMethods)); +} + +} // namespace android diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index a87902fe03c5..e7de081bd5f8 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -63,6 +63,7 @@ int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env); int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env); int register_android_server_companion_virtual_InputController(JNIEnv* env); int register_android_server_app_GameManagerService(JNIEnv* env); +int register_com_android_server_BootReceiver(JNIEnv* env); int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env); int register_com_android_server_display_DisplayControl(JNIEnv* env); int register_com_android_server_SystemClockTime(JNIEnv* env); @@ -122,6 +123,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_sensor_SensorService(vm, env); register_android_server_companion_virtual_InputController(env); register_android_server_app_GameManagerService(env); + register_com_android_server_BootReceiver(env); register_com_android_server_wm_TaskFpsCallbackController(env); register_com_android_server_display_DisplayControl(env); register_com_android_server_SystemClockTime(env); diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java index 6e82907d4361..fd21a326b640 100644 --- a/services/java/com/android/server/SystemConfigService.java +++ b/services/java/com/android/server/SystemConfigService.java @@ -21,6 +21,8 @@ import static java.util.stream.Collectors.toMap; import android.Manifest; import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManagerInternal; +import android.os.Binder; import android.os.ISystemConfig; import android.util.ArrayMap; import android.util.ArraySet; @@ -108,6 +110,15 @@ public class SystemConfigService extends SystemService { "Caller must hold " + Manifest.permission.QUERY_ALL_PACKAGES); return new ArrayList<>(SystemConfig.getInstance().getDefaultVrComponents()); } + + @Override + public List<String> getPreventUserDisablePackages() { + PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); + return SystemConfig.getInstance().getPreventUserDisablePackages().stream() + .filter(preventUserDisablePackage -> + pmi.canQueryPackage(Binder.getCallingUid(), preventUserDisablePackage)) + .collect(toList()); + } }; public SystemConfigService(Context context) { diff --git a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java index c42c735e3c9d..37ca09d9fa27 100644 --- a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java @@ -32,6 +32,7 @@ import static com.google.common.truth.Truth.assertThat; import android.Manifest; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; import android.app.trust.ITrustListener; import android.app.trust.ITrustManager; @@ -45,7 +46,8 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; -import android.net.Uri; +import android.hardware.biometrics.BiometricManager; +import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -53,7 +55,12 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; +import android.security.Authorization; +import android.security.authorization.IKeystoreAuthorization; import android.service.trust.TrustAgentService; import android.testing.TestableContext; import android.view.IWindowManager; @@ -83,23 +90,34 @@ public class TrustManagerServiceTest { @Rule public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this) + .spyStatic(ActivityManager.class) + .spyStatic(Authorization.class) .mockStatic(ServiceManager.class) .mockStatic(WindowManagerGlobal.class) .build(); @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Rule public final MockContext mMockContext = new MockContext( ApplicationProvider.getApplicationContext()); private static final String URI_SCHEME_PACKAGE = "package"; private static final int TEST_USER_ID = 50; + private static final int PARENT_USER_ID = 60; + private static final int PROFILE_USER_ID = 70; + private static final long[] PARENT_BIOMETRIC_SIDS = new long[] { 600L, 601L }; + private static final long[] PROFILE_BIOMETRIC_SIDS = new long[] { 700L, 701L }; private final ArrayList<ResolveInfo> mTrustAgentResolveInfoList = new ArrayList<>(); private final ArrayList<ComponentName> mKnownTrustAgents = new ArrayList<>(); private final ArrayList<ComponentName> mEnabledTrustAgents = new ArrayList<>(); private @Mock ActivityManager mActivityManager; + private @Mock BiometricManager mBiometricManager; private @Mock DevicePolicyManager mDevicePolicyManager; + private @Mock IKeystoreAuthorization mKeystoreAuthorization; private @Mock LockPatternUtils mLockPatternUtils; private @Mock PackageManager mPackageManager; private @Mock UserManager mUserManager; @@ -113,6 +131,9 @@ public class TrustManagerServiceTest { @Before public void setUp() throws Exception { when(mActivityManager.isUserRunning(TEST_USER_ID)).thenReturn(true); + doReturn(mock(IActivityManager.class)).when(() -> ActivityManager.getService()); + + doReturn(mKeystoreAuthorization).when(() -> Authorization.getService()); when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager); when(mLockPatternUtils.isSecure(TEST_USER_ID)).thenReturn(true); @@ -146,6 +167,7 @@ public class TrustManagerServiceTest { when(mWindowManager.isKeyguardLocked()).thenReturn(true); mMockContext.addMockSystemService(ActivityManager.class, mActivityManager); + mMockContext.addMockSystemService(BiometricManager.class, mBiometricManager); mMockContext.setMockPackageManager(mPackageManager); mMockContext.addMockSystemService(UserManager.class, mUserManager); doReturn(mWindowManager).when(() -> WindowManagerGlobal.getWindowManagerService()); @@ -257,7 +279,7 @@ public class TrustManagerServiceTest { "com.android/.SystemTrustAgent"); addTrustAgent(newAgentComponentName, /* isSystemApp= */ true); - mMockContext.sendPackageChangedBroadcast(newAgentComponentName); + notifyPackageChanged(newAgentComponentName); assertThat(mEnabledTrustAgents).containsExactly(newAgentComponentName); assertThat(mKnownTrustAgents).containsExactly(newAgentComponentName); @@ -276,7 +298,7 @@ public class TrustManagerServiceTest { "com.android/.SystemTrustAgent"); addTrustAgent(newAgentComponentName, /* isSystemApp= */ true); - mMockContext.sendPackageChangedBroadcast(newAgentComponentName); + notifyPackageChanged(newAgentComponentName); assertThat(mEnabledTrustAgents).containsExactly(defaultTrustAgent); assertThat(mKnownTrustAgents).containsExactly(defaultTrustAgent, newAgentComponentName); @@ -289,7 +311,7 @@ public class TrustManagerServiceTest { "com.user/.UserTrustAgent"); addTrustAgent(newAgentComponentName, /* isSystemApp= */ false); - mMockContext.sendPackageChangedBroadcast(newAgentComponentName); + notifyPackageChanged(newAgentComponentName); assertThat(mEnabledTrustAgents).isEmpty(); assertThat(mKnownTrustAgents).containsExactly(newAgentComponentName); @@ -307,7 +329,7 @@ public class TrustManagerServiceTest { // Simulate user turning off systemTrustAgent2 mLockPatternUtils.setEnabledTrustAgents(List.of(systemTrustAgent1), TEST_USER_ID); - mMockContext.sendPackageChangedBroadcast(systemTrustAgent2); + notifyPackageChanged(systemTrustAgent2); assertThat(mEnabledTrustAgents).containsExactly(systemTrustAgent1); } @@ -322,6 +344,73 @@ public class TrustManagerServiceTest { verify(trustListener).onEnabledTrustAgentsChanged(TEST_USER_ID); } + // Tests that when the device is locked for a managed profile with a *unified* challenge, the + // device locked notification that is sent to Keystore contains the biometric SIDs of the parent + // user, not the profile. This matches the authentication that is needed to unlock the device + // for the profile again. + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void testLockDeviceForManagedProfileWithUnifiedChallenge_usesParentBiometricSids() + throws Exception { + setupMocksForProfile(/* unifiedChallenge= */ true); + + when(mWindowManager.isKeyguardLocked()).thenReturn(false); + mTrustManager.reportKeyguardShowingChanged(); + verify(mKeystoreAuthorization).onDeviceUnlocked(PARENT_USER_ID, null); + verify(mKeystoreAuthorization).onDeviceUnlocked(PROFILE_USER_ID, null); + + when(mWindowManager.isKeyguardLocked()).thenReturn(true); + mTrustManager.reportKeyguardShowingChanged(); + verify(mKeystoreAuthorization) + .onDeviceLocked(eq(PARENT_USER_ID), eq(PARENT_BIOMETRIC_SIDS)); + verify(mKeystoreAuthorization) + .onDeviceLocked(eq(PROFILE_USER_ID), eq(PARENT_BIOMETRIC_SIDS)); + } + + // Tests that when the device is locked for a managed profile with a *separate* challenge, the + // device locked notification that is sent to Keystore contains the biometric SIDs of the + // profile itself. This matches the authentication that is needed to unlock the device for the + // profile again. + @Test + public void testLockDeviceForManagedProfileWithSeparateChallenge_usesProfileBiometricSids() + throws Exception { + setupMocksForProfile(/* unifiedChallenge= */ false); + + mTrustManager.setDeviceLockedForUser(PROFILE_USER_ID, false); + verify(mKeystoreAuthorization).onDeviceUnlocked(PROFILE_USER_ID, null); + + mTrustManager.setDeviceLockedForUser(PROFILE_USER_ID, true); + verify(mKeystoreAuthorization) + .onDeviceLocked(eq(PROFILE_USER_ID), eq(PROFILE_BIOMETRIC_SIDS)); + } + + private void setupMocksForProfile(boolean unifiedChallenge) { + UserInfo parent = new UserInfo(PARENT_USER_ID, "parent", UserInfo.FLAG_FULL); + UserInfo profile = new UserInfo(PROFILE_USER_ID, "profile", UserInfo.FLAG_MANAGED_PROFILE); + when(mUserManager.getAliveUsers()).thenReturn(List.of(parent, profile)); + when(mUserManager.getUserInfo(PARENT_USER_ID)).thenReturn(parent); + when(mUserManager.getUserInfo(PROFILE_USER_ID)).thenReturn(profile); + when(mUserManager.getProfileParent(PROFILE_USER_ID)).thenReturn(parent); + when(mUserManager.getEnabledProfileIds(PARENT_USER_ID)) + .thenReturn(new int[] { PROFILE_USER_ID }); + + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true); + when(mLockPatternUtils.isProfileWithUnifiedChallenge(PROFILE_USER_ID)) + .thenReturn(unifiedChallenge); + when(mLockPatternUtils.isManagedProfileWithUnifiedChallenge(PROFILE_USER_ID)) + .thenReturn(unifiedChallenge); + when(mLockPatternUtils.isSeparateProfileChallengeEnabled(PROFILE_USER_ID)) + .thenReturn(!unifiedChallenge); + + when(mBiometricManager.getAuthenticatorIds(PARENT_USER_ID)) + .thenReturn(PARENT_BIOMETRIC_SIDS); + when(mBiometricManager.getAuthenticatorIds(PROFILE_USER_ID)) + .thenReturn(PROFILE_BIOMETRIC_SIDS); + + bootService(); + mService.onUserSwitching(null, new SystemService.TargetUser(parent)); + } + private void addTrustAgent(ComponentName agentComponentName, boolean isSystemApp) { ApplicationInfo applicationInfo = new ApplicationInfo(); if (isSystemApp) { @@ -350,11 +439,16 @@ public class TrustManagerServiceTest { permission, PackageManager.PERMISSION_GRANTED); } + private void notifyPackageChanged(ComponentName changedComponent) { + mService.mPackageMonitor.onPackageChanged( + changedComponent.getPackageName(), + UserHandle.of(TEST_USER_ID).getUid(1234), + new String[] { changedComponent.getClassName() }); + } + /** A mock Context that allows the test process to send protected broadcasts. */ private static final class MockContext extends TestableContext { - private final ArrayList<BroadcastReceiver> mPackageChangedBroadcastReceivers = - new ArrayList<>(); private final ArrayList<BroadcastReceiver> mUserStartedBroadcastReceivers = new ArrayList<>(); @@ -368,9 +462,6 @@ public class TrustManagerServiceTest { UserHandle user, IntentFilter filter, @Nullable String broadcastPermission, @Nullable Handler scheduler) { - if (filter.hasAction(Intent.ACTION_PACKAGE_CHANGED)) { - mPackageChangedBroadcastReceivers.add(receiver); - } if (filter.hasAction(Intent.ACTION_USER_STARTED)) { mUserStartedBroadcastReceivers.add(receiver); } @@ -378,18 +469,9 @@ public class TrustManagerServiceTest { scheduler); } - void sendPackageChangedBroadcast(ComponentName changedComponent) { - Intent intent = new Intent( - Intent.ACTION_PACKAGE_CHANGED, - Uri.fromParts(URI_SCHEME_PACKAGE, - changedComponent.getPackageName(), /* fragment= */ null)) - .putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, - new String[]{changedComponent.getClassName()}) - .putExtra(Intent.EXTRA_USER_HANDLE, TEST_USER_ID) - .putExtra(Intent.EXTRA_UID, UserHandle.of(TEST_USER_ID).getUid(1234)); - for (BroadcastReceiver receiver : mPackageChangedBroadcastReceivers) { - receiver.onReceive(this, intent); - } + @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user, + @Nullable String receiverPermission, @Nullable Bundle options) { } void sendUserStartedBroadcast() { diff --git a/services/tests/servicestests/src/com/android/server/BootReceiverTest.java b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java new file mode 100644 index 000000000000..523c5c060cf5 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static com.google.common.truth.Truth.assertThat; + +import android.test.AndroidTestCase; + +import com.android.server.os.TombstoneProtos; +import com.android.server.os.TombstoneProtos.Tombstone; + +public class BootReceiverTest extends AndroidTestCase { + private static final String TAG = "BootReceiverTest"; + + public void testRemoveMemoryFromTombstone() { + Tombstone tombstoneBase = Tombstone.newBuilder() + .setBuildFingerprint("build_fingerprint") + .setRevision("revision") + .setPid(123) + .setTid(23) + .setUid(34) + .setSelinuxLabel("selinux_label") + .addCommandLine("cmd1") + .addCommandLine("cmd2") + .addCommandLine("cmd3") + .setProcessUptime(300) + .setAbortMessage("abort") + .addCauses(TombstoneProtos.Cause.newBuilder() + .setHumanReadable("cause1") + .setMemoryError(TombstoneProtos.MemoryError.newBuilder() + .setTool(TombstoneProtos.MemoryError.Tool.SCUDO) + .setType(TombstoneProtos.MemoryError.Type.DOUBLE_FREE))) + .addLogBuffers(TombstoneProtos.LogBuffer.newBuilder().setName("name").addLogs( + TombstoneProtos.LogMessage.newBuilder() + .setTimestamp("123") + .setMessage("message"))) + .addOpenFds(TombstoneProtos.FD.newBuilder().setFd(1).setPath("path")) + .build(); + + Tombstone tombstoneWithoutMemory = tombstoneBase.toBuilder() + .putThreads(1, TombstoneProtos.Thread.newBuilder() + .setId(1) + .setName("thread1") + .addRegisters(TombstoneProtos.Register.newBuilder().setName("r1").setU64(1)) + .addRegisters(TombstoneProtos.Register.newBuilder().setName("r2").setU64(2)) + .addBacktraceNote("backtracenote1") + .addUnreadableElfFiles("files1") + .setTaggedAddrCtrl(1) + .setPacEnabledKeys(10) + .build()) + .build(); + + Tombstone tombstoneWithMemory = tombstoneBase.toBuilder() + .addMemoryMappings(TombstoneProtos.MemoryMapping.newBuilder() + .setBeginAddress(1) + .setEndAddress(100) + .setOffset(10) + .setRead(true) + .setWrite(true) + .setExecute(false) + .setMappingName("mapping") + .setBuildId("build") + .setLoadBias(70)) + .putThreads(1, TombstoneProtos.Thread.newBuilder() + .setId(1) + .setName("thread1") + .addRegisters(TombstoneProtos.Register.newBuilder().setName("r1").setU64(1)) + .addRegisters(TombstoneProtos.Register.newBuilder().setName("r2").setU64(2)) + .addBacktraceNote("backtracenote1") + .addUnreadableElfFiles("files1") + .addMemoryDump(TombstoneProtos.MemoryDump.newBuilder() + .setRegisterName("register1") + .setMappingName("mapping") + .setBeginAddress(10)) + .setTaggedAddrCtrl(1) + .setPacEnabledKeys(10) + .build()) + .build(); + + assertThat(BootReceiver.removeMemoryFromTombstone(tombstoneWithMemory)) + .isEqualTo(tombstoneWithoutMemory); + } +} |