diff options
64 files changed, 983 insertions, 688 deletions
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index d59270c6a51b..96110e1541f8 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -15,6 +15,7 @@ */ package android.media; +import android.annotation.CheckResult; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.Uri; @@ -32,6 +33,7 @@ import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap.SeekPoints; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.amr.AmrExtractor; +import com.google.android.exoplayer2.extractor.flac.FlacExtractor; import com.google.android.exoplayer2.extractor.flv.FlvExtractor; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor; @@ -382,6 +384,7 @@ public final class MediaParser { * parse the input. */ @NonNull + @CheckResult private static UnrecognizedInputFormatException createForExtractors( @NonNull String... extractorNames) { StringBuilder builder = new StringBuilder(); @@ -536,7 +539,7 @@ public final class MediaParser { } } if (mExtractor == null) { - UnrecognizedInputFormatException.createForExtractors(mExtractorNamesPool); + throw UnrecognizedInputFormatException.createForExtractors(mExtractorNamesPool); } return true; } @@ -912,6 +915,7 @@ public final class MediaParser { extractorFactoriesByName.put("exo.Ac4Extractor", Ac4Extractor::new); extractorFactoriesByName.put("exo.AdtsExtractor", AdtsExtractor::new); extractorFactoriesByName.put("exo.AmrExtractor", AmrExtractor::new); + extractorFactoriesByName.put("exo.FlacExtractor", FlacExtractor::new); extractorFactoriesByName.put("exo.FlvExtractor", FlvExtractor::new); extractorFactoriesByName.put("exo.FragmentedMp4Extractor", FragmentedMp4Extractor::new); extractorFactoriesByName.put("exo.MatroskaExtractor", MatroskaExtractor::new); diff --git a/api/current.txt b/api/current.txt index 02fac79675dc..81c4c922b730 100644 --- a/api/current.txt +++ b/api/current.txt @@ -35687,6 +35687,7 @@ package android.os { field public static final String INCREMENTAL; field public static final int PREVIEW_SDK_INT; field public static final String RELEASE; + field @NonNull public static final String RELEASE_OR_CODENAME; field @Deprecated public static final String SDK; field public static final int SDK_INT; field public static final String SECURITY_PATCH; diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 73befec72274..3a2472efb601 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -65,7 +65,6 @@ cc_defaults { "src/config/ConfigListener.cpp", "src/config/ConfigManager.cpp", "src/experiment_ids.proto", - "src/external/GpuStatsPuller.cpp", "src/external/Perfetto.cpp", "src/external/PullResultReceiver.cpp", "src/external/puller_util.cpp", @@ -111,7 +110,7 @@ cc_defaults { ], cflags: [ - // "-DNEW_ENCODING_SCHEME", + "-DNEW_ENCODING_SCHEME", ], local_include_dirs: [ @@ -125,16 +124,15 @@ cc_defaults { "libprotoutil", "libstatslog", "libstatsmetadata", - "libstatssocket", "libsysutils", ], shared_libs: [ "libbinder", - "libgraphicsenv", "libhidlbase", "libincident", "liblog", "libservices", + "libstatssocket", "libutils", ], } @@ -278,7 +276,6 @@ cc_test { "tests/e2e/PartialBucket_e2e_test.cpp", "tests/e2e/ValueMetric_pull_e2e_test.cpp", "tests/e2e/WakelockDuration_e2e_test.cpp", - "tests/external/GpuStatsPuller_test.cpp", "tests/external/puller_util_test.cpp", "tests/external/StatsCallbackPuller_test.cpp", "tests/external/StatsPuller_test.cpp", diff --git a/cmds/statsd/src/external/GpuStatsPuller.cpp b/cmds/statsd/src/external/GpuStatsPuller.cpp deleted file mode 100644 index 3229ba82fe3c..000000000000 --- a/cmds/statsd/src/external/GpuStatsPuller.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "GpuStatsPuller.h" - -#include <binder/IServiceManager.h> -#include <graphicsenv/GpuStatsInfo.h> -#include <graphicsenv/IGpuService.h> - -#include "logd/LogEvent.h" - -#include "stats_log_util.h" -#include "statslog.h" - -namespace android { -namespace os { -namespace statsd { - -using android::util::ProtoReader; - -GpuStatsPuller::GpuStatsPuller(const int tagId) : StatsPuller(tagId) { -} - -static sp<IGpuService> getGpuService() { - const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu")); - if (!binder) { - ALOGE("Failed to get gpu service"); - return nullptr; - } - - return interface_cast<IGpuService>(binder); -} - -static bool pullGpuStatsGlobalInfo(const sp<IGpuService>& gpuService, - std::vector<std::shared_ptr<LogEvent>>* data) { - std::vector<GpuStatsGlobalInfo> stats; - status_t status = gpuService->getGpuStatsGlobalInfo(&stats); - if (status != OK) { - return false; - } - - data->clear(); - data->reserve(stats.size()); - for (const auto& info : stats) { - std::shared_ptr<LogEvent> event = make_shared<LogEvent>( - android::util::GPU_STATS_GLOBAL_INFO, getWallClockNs(), getElapsedRealtimeNs()); - if (!event->write(info.driverPackageName)) return false; - if (!event->write(info.driverVersionName)) return false; - if (!event->write((int64_t)info.driverVersionCode)) return false; - if (!event->write(info.driverBuildTime)) return false; - if (!event->write((int64_t)info.glLoadingCount)) return false; - if (!event->write((int64_t)info.glLoadingFailureCount)) return false; - if (!event->write((int64_t)info.vkLoadingCount)) return false; - if (!event->write((int64_t)info.vkLoadingFailureCount)) return false; - if (!event->write(info.vulkanVersion)) return false; - if (!event->write(info.cpuVulkanVersion)) return false; - if (!event->write(info.glesVersion)) return false; - if (!event->write((int64_t)info.angleLoadingCount)) return false; - if (!event->write((int64_t)info.angleLoadingFailureCount)) return false; - event->init(); - data->emplace_back(event); - } - - return true; -} - -static bool pullGpuStatsAppInfo(const sp<IGpuService>& gpuService, - std::vector<std::shared_ptr<LogEvent>>* data) { - std::vector<GpuStatsAppInfo> stats; - status_t status = gpuService->getGpuStatsAppInfo(&stats); - if (status != OK) { - return false; - } - - data->clear(); - data->reserve(stats.size()); - for (const auto& info : stats) { - std::shared_ptr<LogEvent> event = make_shared<LogEvent>( - android::util::GPU_STATS_APP_INFO, getWallClockNs(), getElapsedRealtimeNs()); - if (!event->write(info.appPackageName)) return false; - if (!event->write((int64_t)info.driverVersionCode)) return false; - if (!event->writeBytes(int64VectorToProtoByteString(info.glDriverLoadingTime))) { - return false; - } - if (!event->writeBytes(int64VectorToProtoByteString(info.vkDriverLoadingTime))) { - return false; - } - if (!event->writeBytes(int64VectorToProtoByteString(info.angleDriverLoadingTime))) { - return false; - } - if (!event->write(info.cpuVulkanInUse)) return false; - if (!event->write(info.falsePrerotation)) return false; - if (!event->write(info.gles1InUse)) return false; - event->init(); - data->emplace_back(event); - } - - return true; -} - -bool GpuStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) { - const sp<IGpuService> gpuService = getGpuService(); - if (!gpuService) { - return false; - } - - switch (mTagId) { - case android::util::GPU_STATS_GLOBAL_INFO: - return pullGpuStatsGlobalInfo(gpuService, data); - case android::util::GPU_STATS_APP_INFO: - return pullGpuStatsAppInfo(gpuService, data); - default: - break; - } - - return false; -} - -static std::string protoOutputStreamToByteString(ProtoOutputStream& proto) { - if (!proto.size()) return ""; - - std::string byteString; - sp<ProtoReader> reader = proto.data(); - while (reader->readBuffer() != nullptr) { - const size_t toRead = reader->currentToRead(); - byteString.append((char*)reader->readBuffer(), toRead); - reader->move(toRead); - } - - if (byteString.size() != proto.size()) return ""; - - return byteString; -} - -std::string int64VectorToProtoByteString(const std::vector<int64_t>& value) { - if (value.empty()) return ""; - - ProtoOutputStream proto; - for (const auto& ele : value) { - proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED | - 1 /* field id */, - (long long)ele); - } - - return protoOutputStreamToByteString(proto); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/GpuStatsPuller.h b/cmds/statsd/src/external/GpuStatsPuller.h deleted file mode 100644 index 2da199c51e0f..000000000000 --- a/cmds/statsd/src/external/GpuStatsPuller.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "StatsPuller.h" - -namespace android { -namespace os { -namespace statsd { - -/** - * Pull GpuStats from GpuService. - */ -class GpuStatsPuller : public StatsPuller { -public: - explicit GpuStatsPuller(const int tagId); - bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override; -}; - -// convert a int64_t vector into a byte string for proto message like: -// message RepeatedInt64Wrapper { -// repeated int64 value = 1; -// } -std::string int64VectorToProtoByteString(const std::vector<int64_t>& value); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 15d7e33d63fd..3ceff7529440 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -32,7 +32,6 @@ #include "../logd/LogEvent.h" #include "../stats_log_util.h" #include "../statscompanion_util.h" -#include "GpuStatsPuller.h" #include "StatsCallbackPuller.h" #include "TrainInfoPuller.h" #include "statslog.h" @@ -51,15 +50,6 @@ StatsPullerManager::StatsPullerManager() : kAllPullAtomInfo({ // TrainInfo. {{.atomTag = android::util::TRAIN_INFO}, new TrainInfoPuller()}, - - // GpuStatsGlobalInfo - {{.atomTag = android::util::GPU_STATS_GLOBAL_INFO}, - new GpuStatsPuller(android::util::GPU_STATS_GLOBAL_INFO)}, - - // GpuStatsAppInfo - {{.atomTag = android::util::GPU_STATS_APP_INFO}, - new GpuStatsPuller(android::util::GPU_STATS_APP_INFO)}, - }), mNextPullTimeNs(NO_ALARM_UPDATE) { } diff --git a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp b/cmds/statsd/tests/external/GpuStatsPuller_test.cpp deleted file mode 100644 index ae92705aff4c..000000000000 --- a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef LOG_TAG -#define LOG_TAG "GpuStatsPuller_test" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <graphicsenv/GpuStatsInfo.h> -#include <log/log.h> - -#include "src/external/GpuStatsPuller.h" -#include "statslog.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -// clang-format off -static const std::string DRIVER_PACKAGE_NAME = "TEST_DRIVER"; -static const std::string DRIVER_VERSION_NAME = "TEST_DRIVER_VERSION"; -static const std::string APP_PACKAGE_NAME = "TEST_APP"; -static const int64_t TIMESTAMP_WALLCLOCK = 111; -static const int64_t TIMESTAMP_ELAPSED = 222; -static const int64_t DRIVER_VERSION_CODE = 333; -static const int64_t DRIVER_BUILD_TIME = 444; -static const int64_t GL_LOADING_COUNT = 3; -static const int64_t GL_LOADING_FAILURE_COUNT = 1; -static const int64_t VK_LOADING_COUNT = 4; -static const int64_t VK_LOADING_FAILURE_COUNT = 0; -static const int64_t ANGLE_LOADING_COUNT = 2; -static const int64_t ANGLE_LOADING_FAILURE_COUNT = 1; -static const int64_t GL_DRIVER_LOADING_TIME_0 = 555; -static const int64_t GL_DRIVER_LOADING_TIME_1 = 666; -static const int64_t VK_DRIVER_LOADING_TIME_0 = 777; -static const int64_t VK_DRIVER_LOADING_TIME_1 = 888; -static const int64_t VK_DRIVER_LOADING_TIME_2 = 999; -static const int64_t ANGLE_DRIVER_LOADING_TIME_0 = 1010; -static const int64_t ANGLE_DRIVER_LOADING_TIME_1 = 1111; -static const int32_t VULKAN_VERSION = 1; -static const int32_t CPU_VULKAN_VERSION = 2; -static const int32_t GLES_VERSION = 3; -static const bool CPU_VULKAN_IN_USE = true; -static const bool FALSE_PREROTATION = true; -static const bool GLES_1_IN_USE = true; -static const size_t NUMBER_OF_VALUES_GLOBAL = 13; -static const size_t NUMBER_OF_VALUES_APP = 8; -// clang-format on - -class MockGpuStatsPuller : public GpuStatsPuller { -public: - MockGpuStatsPuller(const int tagId, vector<std::shared_ptr<LogEvent>>* data) - : GpuStatsPuller(tagId), mData(data){}; - -private: - bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override { - *data = *mData; - return true; - } - - vector<std::shared_ptr<LogEvent>>* mData; -}; - -class GpuStatsPuller_test : public ::testing::Test { -public: - GpuStatsPuller_test() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - } - - ~GpuStatsPuller_test() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); - } -}; - -TEST_F(GpuStatsPuller_test, PullGpuStatsGlobalInfo) { - vector<std::shared_ptr<LogEvent>> inData, outData; - std::shared_ptr<LogEvent> event = make_shared<LogEvent>(android::util::GPU_STATS_GLOBAL_INFO, - TIMESTAMP_WALLCLOCK, TIMESTAMP_ELAPSED); - EXPECT_TRUE(event->write(DRIVER_PACKAGE_NAME)); - EXPECT_TRUE(event->write(DRIVER_VERSION_NAME)); - EXPECT_TRUE(event->write(DRIVER_VERSION_CODE)); - EXPECT_TRUE(event->write(DRIVER_BUILD_TIME)); - EXPECT_TRUE(event->write(GL_LOADING_COUNT)); - EXPECT_TRUE(event->write(GL_LOADING_FAILURE_COUNT)); - EXPECT_TRUE(event->write(VK_LOADING_COUNT)); - EXPECT_TRUE(event->write(VK_LOADING_FAILURE_COUNT)); - EXPECT_TRUE(event->write(VULKAN_VERSION)); - EXPECT_TRUE(event->write(CPU_VULKAN_VERSION)); - EXPECT_TRUE(event->write(GLES_VERSION)); - EXPECT_TRUE(event->write(ANGLE_LOADING_COUNT)); - EXPECT_TRUE(event->write(ANGLE_LOADING_FAILURE_COUNT)); - event->init(); - inData.emplace_back(event); - MockGpuStatsPuller mockPuller(android::util::GPU_STATS_GLOBAL_INFO, &inData); - mockPuller.ForceClearCache(); - mockPuller.Pull(&outData); - - ASSERT_EQ(1, outData.size()); - EXPECT_EQ(android::util::GPU_STATS_GLOBAL_INFO, outData[0]->GetTagId()); - ASSERT_EQ(NUMBER_OF_VALUES_GLOBAL, outData[0]->size()); - EXPECT_EQ(DRIVER_PACKAGE_NAME, outData[0]->getValues()[0].mValue.str_value); - EXPECT_EQ(DRIVER_VERSION_NAME, outData[0]->getValues()[1].mValue.str_value); - EXPECT_EQ(DRIVER_VERSION_CODE, outData[0]->getValues()[2].mValue.long_value); - EXPECT_EQ(DRIVER_BUILD_TIME, outData[0]->getValues()[3].mValue.long_value); - EXPECT_EQ(GL_LOADING_COUNT, outData[0]->getValues()[4].mValue.long_value); - EXPECT_EQ(GL_LOADING_FAILURE_COUNT, outData[0]->getValues()[5].mValue.long_value); - EXPECT_EQ(VK_LOADING_COUNT, outData[0]->getValues()[6].mValue.long_value); - EXPECT_EQ(VK_LOADING_FAILURE_COUNT, outData[0]->getValues()[7].mValue.long_value); - EXPECT_EQ(VULKAN_VERSION, outData[0]->getValues()[8].mValue.int_value); - EXPECT_EQ(CPU_VULKAN_VERSION, outData[0]->getValues()[9].mValue.int_value); - EXPECT_EQ(GLES_VERSION, outData[0]->getValues()[10].mValue.int_value); - EXPECT_EQ(ANGLE_LOADING_COUNT, outData[0]->getValues()[11].mValue.long_value); - EXPECT_EQ(ANGLE_LOADING_FAILURE_COUNT, outData[0]->getValues()[12].mValue.long_value); -} - -TEST_F(GpuStatsPuller_test, PullGpuStatsAppInfo) { - vector<std::shared_ptr<LogEvent>> inData, outData; - std::shared_ptr<LogEvent> event = make_shared<LogEvent>(android::util::GPU_STATS_APP_INFO, - TIMESTAMP_WALLCLOCK, TIMESTAMP_ELAPSED); - EXPECT_TRUE(event->write(APP_PACKAGE_NAME)); - EXPECT_TRUE(event->write(DRIVER_VERSION_CODE)); - std::vector<int64_t> glDriverLoadingTime; - glDriverLoadingTime.emplace_back(GL_DRIVER_LOADING_TIME_0); - glDriverLoadingTime.emplace_back(GL_DRIVER_LOADING_TIME_1); - std::vector<int64_t> vkDriverLoadingTime; - vkDriverLoadingTime.emplace_back(VK_DRIVER_LOADING_TIME_0); - vkDriverLoadingTime.emplace_back(VK_DRIVER_LOADING_TIME_1); - vkDriverLoadingTime.emplace_back(VK_DRIVER_LOADING_TIME_2); - std::vector<int64_t> angleDriverLoadingTime; - angleDriverLoadingTime.emplace_back(ANGLE_DRIVER_LOADING_TIME_0); - angleDriverLoadingTime.emplace_back(ANGLE_DRIVER_LOADING_TIME_1); - EXPECT_TRUE(event->write(int64VectorToProtoByteString(glDriverLoadingTime))); - EXPECT_TRUE(event->write(int64VectorToProtoByteString(vkDriverLoadingTime))); - EXPECT_TRUE(event->write(int64VectorToProtoByteString(angleDriverLoadingTime))); - EXPECT_TRUE(event->write(CPU_VULKAN_IN_USE)); - EXPECT_TRUE(event->write(FALSE_PREROTATION)); - EXPECT_TRUE(event->write(GLES_1_IN_USE)); - event->init(); - inData.emplace_back(event); - MockGpuStatsPuller mockPuller(android::util::GPU_STATS_APP_INFO, &inData); - mockPuller.ForceClearCache(); - mockPuller.Pull(&outData); - - ASSERT_EQ(1, outData.size()); - EXPECT_EQ(android::util::GPU_STATS_APP_INFO, outData[0]->GetTagId()); - ASSERT_EQ(NUMBER_OF_VALUES_APP, outData[0]->size()); - EXPECT_EQ(APP_PACKAGE_NAME, outData[0]->getValues()[0].mValue.str_value); - EXPECT_EQ(DRIVER_VERSION_CODE, outData[0]->getValues()[1].mValue.long_value); - EXPECT_EQ(int64VectorToProtoByteString(glDriverLoadingTime), - outData[0]->getValues()[2].mValue.str_value); - EXPECT_EQ(int64VectorToProtoByteString(vkDriverLoadingTime), - outData[0]->getValues()[3].mValue.str_value); - EXPECT_EQ(int64VectorToProtoByteString(angleDriverLoadingTime), - outData[0]->getValues()[4].mValue.str_value); - EXPECT_EQ(CPU_VULKAN_IN_USE, outData[0]->getValues()[5].mValue.int_value); - EXPECT_EQ(FALSE_PREROTATION, outData[0]->getValues()[6].mValue.int_value); - EXPECT_EQ(GLES_1_IN_USE, outData[0]->getValues()[7].mValue.int_value); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index a29173469ffb..70b2db70a9e8 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -240,6 +240,13 @@ public class Build { public static final String RELEASE = getString("ro.build.version.release"); /** + * The version string we show to the user; may be {@link #RELEASE} or + * {@link #CODENAME} if not a final release build. + */ + @NonNull public static final String RELEASE_OR_CODENAME = getString( + "ro.build.version.release_or_codename"); + + /** * The base OS build the product is based on. */ public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", ""); diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 44f12a6adf60..21a1e0f0a108 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -34,9 +34,11 @@ import android.text.TextUtils; import android.util.Log; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; +import java.util.List; /** * Provides access to environment variables. @@ -539,12 +541,21 @@ public class Environment { @SystemApi public static @NonNull Collection<File> getInternalMediaDirectories() { final ArrayList<File> res = new ArrayList<>(); - res.add(new File(Environment.getRootDirectory(), "media")); - res.add(new File(Environment.getOemDirectory(), "media")); - res.add(new File(Environment.getProductDirectory(), "media")); + addCanonicalFile(res, new File(Environment.getRootDirectory(), "media")); + addCanonicalFile(res, new File(Environment.getOemDirectory(), "media")); + addCanonicalFile(res, new File(Environment.getProductDirectory(), "media")); return res; } + private static void addCanonicalFile(List<File> list, File file) { + try { + list.add(file.getCanonicalFile()); + } catch (IOException e) { + Log.w(TAG, "Failed to resolve " + file + ": " + e); + list.add(file); + } + } + /** * Return the primary shared/external storage directory. This directory may * not currently be accessible if it has been mounted by the user on their diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 8990bdf06a6f..35fa37a491de 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -23,8 +23,6 @@ import android.annotation.SystemService; import android.content.Context; import android.content.pm.DataLoaderParams; import android.os.RemoteException; -import android.system.ErrnoException; -import android.system.Os; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -193,116 +191,54 @@ public final class IncrementalManager { } /** - * Renames an Incremental path to a new path. If source path is a file, make a link from the old - * Incremental file to the new one. If source path is a dir, unbind old dir from Incremental - * Storage and bind the new one. - * <ol> - * <li> For renaming a dir, dest dir will be created if not exists, and does not need to - * be on the same Incremental storage as the source. </li> - * <li> For renaming a file, dest file must be on the same Incremental storage as source. - * </li> - * </ol> + * Set up an app's code path. The expected outcome of this method is: + * 1) The actual apk directory under /data/incremental is bind-mounted to the parent directory + * of {@code afterCodeFile}. + * 2) All the files under {@code beforeCodeFile} will show up under {@code afterCodeFile}. * - * @param sourcePath Absolute path to the source. Should be the same type as the destPath (file - * or dir). Expected to already exist and is an Incremental path. - * @param destPath Absolute path to the destination. - * @throws IllegalArgumentException when 1) source does not exist, or 2) source and dest type - * mismatch (one is file and the other is dir), or 3) source - * path is not on Incremental File System, - * @throws IOException when 1) cannot find the root path of the Incremental storage - * of source, or 2) cannot retrieve the Incremental storage - * instance of the source, or 3) renaming a file, but dest is - * not on the same Incremental Storage, or 4) renaming a dir, - * dest dir does not exist but fails to be created. - * <p> - * TODO(b/136132412): add unit tests + * @param beforeCodeFile Path that is currently bind-mounted and have APKs under it. + * Should no longer have any APKs after this method is called. + * Example: /data/app/vmdl*tmp + * @param afterCodeFile Path that should will have APKs after this method is called. Its parent + * directory should be bind-mounted to a directory under /data/incremental. + * Example: /data/app/~~[randomStringA]/[packageName]-[randomStringB] + * @throws IllegalArgumentException + * @throws IOException + * TODO(b/147371381): add unit tests */ - public void rename(@NonNull String sourcePath, @NonNull String destPath) throws IOException { - final File source = new File(sourcePath); - final File dest = new File(destPath); - if (!source.exists()) { - throw new IllegalArgumentException("Path not exist: " + sourcePath); + public void renameCodePath(File beforeCodeFile, File afterCodeFile) + throws IllegalArgumentException, IOException { + final String beforeCodePath = beforeCodeFile.getAbsolutePath(); + final String afterCodePathParent = afterCodeFile.getParentFile().getAbsolutePath(); + if (!isIncrementalPath(beforeCodePath)) { + throw new IllegalArgumentException("Not an Incremental path: " + beforeCodePath); } - if (dest.exists()) { - throw new IllegalArgumentException("Target path already exists: " + destPath); + final String afterCodePathName = afterCodeFile.getName(); + final Path apkStoragePath = Paths.get(beforeCodePath); + if (apkStoragePath == null || apkStoragePath.toAbsolutePath() == null) { + throw new IOException("Invalid source storage path for: " + beforeCodePath); } - if (source.isDirectory() && dest.exists() && dest.isFile()) { - throw new IllegalArgumentException( - "Trying to rename a dir but destination is a file: " + destPath); - } - if (source.isFile() && dest.exists() && dest.isDirectory()) { - throw new IllegalArgumentException( - "Trying to rename a file but destination is a dir: " + destPath); - } - if (!isIncrementalPath(sourcePath)) { - throw new IllegalArgumentException("Not an Incremental path: " + sourcePath); - } - - Path storagePath = Paths.get(sourcePath); - if (source.isFile()) { - storagePath = getStoragePathForFile(source); - } - if (storagePath == null || storagePath.toAbsolutePath() == null) { - throw new IOException("Invalid source storage path for: " + sourcePath); - } - final IncrementalStorage storage = openStorage(storagePath.toAbsolutePath().toString()); - if (storage == null) { + final IncrementalStorage apkStorage = + openStorage(apkStoragePath.toAbsolutePath().toString()); + if (apkStorage == null) { throw new IOException("Failed to retrieve storage from Incremental Service."); } - - if (source.isFile()) { - renameFile(storage, storagePath, source, dest); - } else { - renameDir(storage, storagePath, source, dest); - } - } - - private void renameFile(IncrementalStorage storage, Path storagePath, - File source, File dest) throws IOException { - Path sourcePath = source.toPath(); - Path destPath = dest.toPath(); - if (!sourcePath.startsWith(storagePath)) { - throw new IOException("Path: " + source.getAbsolutePath() + " is not on storage at: " - + storagePath.toString()); - } - if (!destPath.startsWith(storagePath)) { - throw new IOException("Path: " + dest.getAbsolutePath() + " is not on storage at: " - + storagePath.toString()); + final IncrementalStorage linkedApkStorage = createStorage(afterCodePathParent, apkStorage, + IncrementalManager.CREATE_MODE_CREATE + | IncrementalManager.CREATE_MODE_PERMANENT_BIND); + if (linkedApkStorage == null) { + throw new IOException("Failed to create linked storage at dir: " + afterCodePathParent); } - final Path sourceRelativePath = storagePath.relativize(sourcePath); - final Path destRelativePath = storagePath.relativize(destPath); - storage.moveFile(sourceRelativePath.toString(), destRelativePath.toString()); - - } - - private void renameDir(IncrementalStorage storage, Path storagePath, - File source, File dest) throws IOException { - Path destPath = dest.toPath(); - boolean usedMkdir = false; - try { - Os.mkdir(dest.getAbsolutePath(), 0755); - usedMkdir = true; - } catch (ErrnoException e) { - // Traditional mkdir fails but maybe we can create it on Incremental File System if - // the dest path is on the same Incremental storage as the source. - if (destPath.startsWith(storagePath)) { - storage.makeDirectories(storagePath.relativize(destPath).toString()); - } else { - throw new IOException("Failed to create directory: " + dest.getAbsolutePath(), e); - } - } - try { - storage.moveDir(source.getAbsolutePath(), dest.getAbsolutePath()); - } catch (Exception ex) { - if (usedMkdir) { - try { - Os.remove(dest.getAbsolutePath()); - } catch (ErrnoException ignored) { - } + linkedApkStorage.makeDirectory(afterCodePathName); + File[] files = beforeCodeFile.listFiles(); + for (int i = 0; i < files.length; i++) { + if (files[i].isFile()) { + String fileName = files[i].getName(); + apkStorage.makeLink( + fileName, linkedApkStorage, afterCodePathName + "/" + fileName); } - throw new IOException( - "Failed to move " + source.getAbsolutePath() + " to " + dest.getAbsolutePath()); } + apkStorage.unBind(beforeCodePath); } /** diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index b6f5138a6582..adfa885f121e 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -5205,6 +5205,7 @@ public final class Telephony { /** * TelephonyProvider column name for allowed network types. Indicate which network types * are allowed. Default is -1. + * <P>Type: BIGINT (long) </P> */ public static final String ALLOWED_NETWORK_TYPES = "allowed_network_types"; } diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java index 848868a5532f..3ff6f549e337 100644 --- a/core/java/android/service/textclassifier/TextClassifierService.java +++ b/core/java/android/service/textclassifier/TextClassifierService.java @@ -41,6 +41,7 @@ import android.util.Slog; import android.view.textclassifier.ConversationActions; import android.view.textclassifier.SelectionEvent; import android.view.textclassifier.TextClassification; +import android.view.textclassifier.TextClassificationConstants; import android.view.textclassifier.TextClassificationContext; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassificationSessionId; @@ -404,22 +405,27 @@ public abstract class TextClassifierService extends Service { */ @NonNull public static TextClassifier getDefaultTextClassifierImplementation(@NonNull Context context) { - final String defaultTextClassifierPackageName = - context.getPackageManager().getDefaultTextClassifierPackageName(); - if (TextUtils.isEmpty(defaultTextClassifierPackageName)) { - return TextClassifier.NO_OP; - } - if (defaultTextClassifierPackageName.equals(context.getPackageName())) { - throw new RuntimeException( - "The default text classifier itself should not call the" - + "getDefaultTextClassifierImplementation() method."); - } final TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class); - if (tcm != null) { + if (tcm == null) { + return TextClassifier.NO_OP; + } + TextClassificationConstants settings = new TextClassificationConstants(); + if (settings.getUseDefaultTextClassifierAsDefaultImplementation()) { + final String defaultTextClassifierPackageName = + context.getPackageManager().getDefaultTextClassifierPackageName(); + if (TextUtils.isEmpty(defaultTextClassifierPackageName)) { + return TextClassifier.NO_OP; + } + if (defaultTextClassifierPackageName.equals(context.getPackageName())) { + throw new RuntimeException( + "The default text classifier itself should not call the" + + "getDefaultTextClassifierImplementation() method."); + } return tcm.getTextClassifier(TextClassifier.DEFAULT_SERVICE); + } else { + return tcm.getTextClassifier(TextClassifier.LOCAL); } - return TextClassifier.NO_OP; } /** @hide **/ diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java index ab2c4fc1bf23..ee3a8a79d5d7 100644 --- a/core/java/android/timezone/CountryTimeZones.java +++ b/core/java/android/timezone/CountryTimeZones.java @@ -18,7 +18,6 @@ package android.timezone; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SuppressLint; import android.icu.util.TimeZone; import java.util.ArrayList; @@ -206,27 +205,47 @@ public final class CountryTimeZones { } /** - * Returns a time zone for the country, if there is one, that matches the desired properties. If - * there are multiple matches and the {@code bias} is one of them then it is returned, otherwise - * an arbitrary match is returned based on the {@link #getEffectiveTimeZoneMappingsAt(long)} - * ordering. + * Returns a time zone for the country, if there is one, that matches the supplied properties. + * If there are multiple matches and the {@code bias} is one of them then it is returned, + * otherwise an arbitrary match is returned based on the {@link + * #getEffectiveTimeZoneMappingsAt(long)} ordering. * + * @param whenMillis the UTC time to match against + * @param bias the time zone to prefer, can be {@code null} to indicate there is no preference * @param totalOffsetMillis the offset from UTC at {@code whenMillis} * @param isDst the Daylight Savings Time state at {@code whenMillis}. {@code true} means DST, - * {@code false} means not DST, {@code null} means unknown - * @param dstOffsetMillis the part of {@code totalOffsetMillis} contributed by DST, only used if - * {@code isDst} is {@code true}. The value can be {@code null} if the DST offset is - * unknown - * @param whenMillis the UTC time to match against - * @param bias the time zone to prefer, can be {@code null} + * {@code false} means not DST + * @return an {@link OffsetResult} with information about a matching zone, or {@code null} if + * there is no match */ @Nullable - public OffsetResult lookupByOffsetWithBias(int totalOffsetMillis, @Nullable Boolean isDst, - @SuppressLint("AutoBoxing") @Nullable Integer dstOffsetMillis, long whenMillis, - @Nullable TimeZone bias) { + public OffsetResult lookupByOffsetWithBias(long whenMillis, @Nullable TimeZone bias, + int totalOffsetMillis, boolean isDst) { libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult = mDelegate.lookupByOffsetWithBias( - totalOffsetMillis, isDst, dstOffsetMillis, whenMillis, bias); + whenMillis, bias, totalOffsetMillis, isDst); + return delegateOffsetResult == null ? null : + new OffsetResult( + delegateOffsetResult.getTimeZone(), delegateOffsetResult.isOnlyMatch()); + } + + /** + * Returns a time zone for the country, if there is one, that matches the supplied properties. + * If there are multiple matches and the {@code bias} is one of them then it is returned, + * otherwise an arbitrary match is returned based on the {@link + * #getEffectiveTimeZoneMappingsAt(long)} ordering. + * + * @param whenMillis the UTC time to match against + * @param bias the time zone to prefer, can be {@code null} to indicate there is no preference + * @param totalOffsetMillis the offset from UTC at {@code whenMillis} + * @return an {@link OffsetResult} with information about a matching zone, or {@code null} if + * there is no match + */ + @Nullable + public OffsetResult lookupByOffsetWithBias(long whenMillis, @Nullable TimeZone bias, + int totalOffsetMillis) { + libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult = + mDelegate.lookupByOffsetWithBias(whenMillis, bias, totalOffsetMillis); return delegateOffsetResult == null ? null : new OffsetResult( delegateOffsetResult.getTimeZone(), delegateOffsetResult.isOnlyMatch()); diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index 36f4e5361ea6..4b3afbaada64 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -80,7 +80,7 @@ public class TimeUtils { return null; } CountryTimeZones.OffsetResult offsetResult = countryTimeZones.lookupByOffsetWithBias( - offsetMillis, isDst, null /* dstOffsetMillis */, whenMillis, bias); + whenMillis, bias, offsetMillis, isDst); return offsetResult != null ? offsetResult.getTimeZone() : null; } diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java index ed69513b7f69..3d5ac58e7704 100644 --- a/core/java/android/view/textclassifier/TextClassificationConstants.java +++ b/core/java/android/view/textclassifier/TextClassificationConstants.java @@ -17,6 +17,7 @@ package android.view.textclassifier; import android.annotation.Nullable; +import android.content.Context; import android.provider.DeviceConfig; import com.android.internal.annotations.VisibleForTesting; @@ -167,6 +168,16 @@ public final class TextClassificationConstants { static final String TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = "textclassifier_service_package_override"; + /** + * Whether to use the default system text classifier as the default text classifier + * implementation. The local text classifier is used if it is {@code false}. + * + * @see android.service.textclassifier.TextClassifierService#getDefaultTextClassifierImplementation(Context) + */ + // TODO: Once the system health experiment is done, remove this together with local TC. + private static final String USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL = + "use_default_system_text_classifier_as_default_impl"; + private static final String DEFAULT_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = null; private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true; private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true; @@ -209,7 +220,8 @@ public final class TextClassificationConstants { private static final boolean TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT = true; private static final boolean TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT = true; private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true; - private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[] {20f, 1.0f, 0.4f}; + private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[]{20f, 1.0f, 0.4f}; + private static final boolean USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL_DEFAULT = true; @Nullable public String getTextClassifierServicePackageOverride() { @@ -331,6 +343,13 @@ public final class TextClassificationConstants { LANG_ID_CONTEXT_SETTINGS, LANG_ID_CONTEXT_SETTINGS_DEFAULT); } + public boolean getUseDefaultTextClassifierAsDefaultImplementation() { + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL, + USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL_DEFAULT); + } + void dump(IndentingPrintWriter pw) { pw.println("TextClassificationConstants:"); pw.increaseIndent(); @@ -378,6 +397,8 @@ public final class TextClassificationConstants { .println(); pw.printPair("textclassifier_service_package_override", getTextClassifierServicePackageOverride()).println(); + pw.printPair("use_default_system_text_classifier_as_default_impl", + getUseDefaultTextClassifierAsDefaultImplementation()).println(); pw.decreaseIndent(); } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index a43e4fe118a9..65cad834d5be 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -2424,6 +2424,10 @@ public class ChooserActivity extends ResolverActivity implements offset += findViewById(R.id.content_preview_container).getHeight(); } + if (hasWorkProfile() && ENABLE_TABBED_VIEW) { + offset += findViewById(R.id.tabs).getHeight(); + } + int directShareHeight = 0; rowsToShow = Math.min(4, rowsToShow); for (int i = 0, childCount = recyclerView.getChildCount(); diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index c6ed624d73ac..518911e652f6 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -323,7 +323,7 @@ public class RuntimeInit { result.append(System.getProperty("java.vm.version")); // such as 1.1.0 result.append(" (Linux; U; Android "); - String version = Build.VERSION.RELEASE; // "1.0" or "3.4b5" + String version = Build.VERSION.RELEASE_OR_CODENAME; // "1.0" or "3.4b5" result.append(version.length() > 0 ? version : "1.0"); // add the model for the release build diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index efa7d591fa38..27d41d4d5ca4 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4878,6 +4878,19 @@ <permission android:name="android.permission.ACCESS_SHARED_LIBRARIES" android:protectionLevel="signature|installer" /> + <!-- Allows an app to log compat change usage. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.LOG_COMPAT_CHANGE" + android:protectionLevel="signature|privileged" /> + <!-- Allows an app to read compat change config. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" + android:protectionLevel="signature|privileged" /> + <!-- Allows an app to override compat change config. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" + android:protectionLevel="signature|privileged" /> + <!-- Allows input events to be monitored. Very dangerous! @hide --> <permission android:name="android.permission.MONITOR_INPUT" android:protectionLevel="signature" /> diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml index d19e313055ae..d6fd7aa30e6e 100644 --- a/core/res/res/layout/accessibility_button_chooser_item.xml +++ b/core/res/res/layout/accessibility_button_chooser_item.xml @@ -20,6 +20,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" + android:clickable="true" android:gravity="center" android:paddingStart="16dp" android:paddingEnd="16dp" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index b2d08a9730fc..31e68e88c0a8 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4343,4 +4343,7 @@ Determines whether the specified key groups can be used to wake up the device. --> <bool name="config_wakeOnDpadKeyPress">true</bool> <bool name="config_wakeOnAssistKeyPress">true</bool> + + <!-- Whether to default to an expanded list of users on the lock screen user switcher. --> + <bool name="config_expandLockScreenUserSwitcher">false</bool> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c59d25f10b0b..2453bb18577f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3863,4 +3863,7 @@ <!-- Toast message for background started foreground service while-in-use permission restriction feature --> <java-symbol type="string" name="allow_while_in_use_permission_in_fgs" /> + + <!-- Whether to expand the lock screen user switcher by default --> + <java-symbol type="bool" name="config_expandLockScreenUserSwitcher" /> </resources> diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index ee93b397bedf..59335a595334 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -111,6 +111,10 @@ <uses-permission android:name="android.permission.MOVE_PACKAGE" /> <uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" /> + <!-- gating and logging permissions --> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" /> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <!-- os storage test permissions --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="android.permission.ASEC_ACCESS" /> diff --git a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java index bf782034c8f1..7733559c156a 100644 --- a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java +++ b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java @@ -90,18 +90,18 @@ public class AtomicFormulaTest { } @Test - public void testValidAtomicFormula_stringValue_appCertificateAutoHashed() { + public void testValidAtomicFormula_stringValue_appCertificateIsNotAutoHashed() { String appCert = "cert"; StringAtomicFormula stringAtomicFormula = new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, appCert); assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.APP_CERTIFICATE); - assertThat(stringAtomicFormula.getValue()).doesNotMatch(appCert); - assertThat(stringAtomicFormula.getIsHashedValue()).isTrue(); + assertThat(stringAtomicFormula.getValue()).matches(appCert); + assertThat(stringAtomicFormula.getIsHashedValue()).isFalse(); } @Test - public void testValidAtomicFormula_stringValue_installerCertificateAutoHashed() { + public void testValidAtomicFormula_stringValue_installerCertificateIsNotAutoHashed() { String installerCert = "cert"; StringAtomicFormula stringAtomicFormula = new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE, @@ -109,8 +109,8 @@ public class AtomicFormulaTest { assertThat(stringAtomicFormula.getKey()).isEqualTo( AtomicFormula.INSTALLER_CERTIFICATE); - assertThat(stringAtomicFormula.getValue()).doesNotMatch(installerCert); - assertThat(stringAtomicFormula.getIsHashedValue()).isTrue(); + assertThat(stringAtomicFormula.getValue()).matches(installerCert); + assertThat(stringAtomicFormula.getIsHashedValue()).isFalse(); } @Test diff --git a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java index c1806028f75b..dc03167f51a9 100644 --- a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java +++ b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java @@ -40,7 +40,7 @@ public class IntegrityFormulaTest { assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.PACKAGE_NAME); assertThat(stringAtomicFormula.getValue()).isEqualTo(packageName); - assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(false); + assertThat(stringAtomicFormula.getIsHashedValue()).isFalse(); } @Test @@ -53,8 +53,8 @@ public class IntegrityFormulaTest { (AtomicFormula.StringAtomicFormula) formula; assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.APP_CERTIFICATE); - assertThat(stringAtomicFormula.getValue()).doesNotMatch(appCertificate); - assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(true); + assertThat(stringAtomicFormula.getValue()).matches(appCertificate); + assertThat(stringAtomicFormula.getIsHashedValue()).isFalse(); } @Test @@ -68,7 +68,7 @@ public class IntegrityFormulaTest { assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.INSTALLER_NAME); assertThat(stringAtomicFormula.getValue()).isEqualTo(installerName); - assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(false); + assertThat(stringAtomicFormula.getIsHashedValue()).isFalse(); } @Test @@ -81,8 +81,8 @@ public class IntegrityFormulaTest { (AtomicFormula.StringAtomicFormula) formula; assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.INSTALLER_CERTIFICATE); - assertThat(stringAtomicFormula.getValue()).doesNotMatch(installerCertificate); - assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(true); + assertThat(stringAtomicFormula.getValue()).matches(installerCertificate); + assertThat(stringAtomicFormula.getIsHashedValue()).isFalse(); } @Test diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java index decc76869a53..2295eb989108 100644 --- a/core/tests/coretests/src/android/os/BuildTest.java +++ b/core/tests/coretests/src/android/os/BuildTest.java @@ -60,7 +60,7 @@ public class BuildTest extends TestCase { assertNotEmpty("BRAND", Build.BRAND); assertNotEmpty("MODEL", Build.MODEL); assertNotEmpty("VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL); - assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE); + assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE_OR_CODENAME); assertNotEmpty("TYPE", Build.TYPE); Assert.assertNotNull("TAGS", Build.TAGS); // TAGS is allowed to be empty. assertNotEmpty("FINGERPRINT", Build.FINGERPRINT); diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index df6b9066ea5c..ce71bebfc455 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -38,6 +38,7 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -355,6 +356,7 @@ public class ChooserActivityTest { // enable the work tab feature flag ResolverActivity.ENABLE_TABBED_VIEW = true; + markWorkProfileUserAvailable(); Intent sendIntent = createSendTextIntent(); List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTestWithOtherProfile(2); @@ -1209,17 +1211,24 @@ public class ChooserActivityTest { // enable the work tab feature flag ResolverActivity.ENABLE_TABBED_VIEW = true; int personalProfileTargets = 3; + int otherProfileTargets = 1; List<ResolvedComponentInfo> personalResolvedComponentInfos = - createResolvedComponentsForTest(personalProfileTargets); + createResolvedComponentsForTestWithOtherProfile( + personalProfileTargets + otherProfileTargets, /* userID */ 10); int workProfileTargets = 4; List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest( workProfileTargets); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos); + Mockito.isA(List.class))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos); + Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); + when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class), + eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendTextIntent(); sendIntent.setType("TestType"); markWorkProfileUserAvailable(); @@ -1229,8 +1238,6 @@ public class ChooserActivityTest { waitForIdle(); assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0)); - // The work list adapter must only be filled when we open the work tab - assertThat(activity.getWorkListAdapter().getCount(), is(0)); onView(withText(R.string.resolver_work_tab)).perform(click()); assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10)); assertThat(activity.getPersonalListAdapter().getCount(), is(personalProfileTargets)); @@ -1243,11 +1250,22 @@ public class ChooserActivityTest { ResolverActivity.ENABLE_TABBED_VIEW = true; markWorkProfileUserAvailable(); int workProfileTargets = 4; + List<ResolvedComponentInfo> personalResolvedComponentInfos = + createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(workProfileTargets); + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos); + when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class), + eq(UserHandle.SYSTEM))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendTextIntent(); sendIntent.setType("TestType"); @@ -1357,6 +1375,20 @@ public class ChooserActivityTest { return infoList; } + private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile( + int numberOfResults, int userId) { + List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); + for (int i = 0; i < numberOfResults; i++) { + if (i == 0) { + infoList.add( + ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId)); + } else { + infoList.add(ResolverDataProvider.createResolvedComponentInfo(i)); + } + } + return infoList; + } + private List<ResolvedComponentInfo> createResolvedComponentsForTestWithUserId( int numberOfResults, int userId) { List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index eee62bb791bf..a68b59086d42 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -16,10 +16,15 @@ package com.android.internal.app; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.annotation.Nullable; +import android.app.prediction.AppPredictionContext; +import android.app.prediction.AppPredictionManager; +import android.app.prediction.AppPredictor; import android.app.usage.UsageStatsManager; import android.content.ContentResolver; import android.content.Context; @@ -40,6 +45,8 @@ import com.android.internal.app.chooser.TargetInfo; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import org.mockito.Mockito; + import java.util.function.Function; public class ChooserWrapperActivity extends ChooserActivity { @@ -173,6 +180,12 @@ public class ChooserWrapperActivity extends ChooserActivity { return mMultiProfilePagerAdapter.getCurrentUserHandle(); } + @Override + public Context createContextAsUser(UserHandle user, int flags) { + // return the current context as a work profile doesn't really exist in these tests + return getApplicationContext(); + } + /** * We cannot directly mock the activity created since instrumentation creates it. * <p> diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java index 911490f30799..5f4194aa51e3 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java @@ -225,6 +225,7 @@ public class ResolverActivityTest { // enable the work tab feature flag ResolverActivity.ENABLE_TABBED_VIEW = true; + markWorkProfileUserAvailable(); Intent sendIntent = createSendImageIntent(); List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTestWithOtherProfile(2); @@ -246,7 +247,6 @@ public class ResolverActivityTest { chosen[0] = targetInfo.getResolveInfo(); return true; }; - // Make a stable copy of the components as the original list may be modified List<ResolvedComponentInfo> stableCopy = createResolvedComponentsForTestWithOtherProfile(2); @@ -443,7 +443,7 @@ public class ResolverActivityTest { // enable the work tab feature flag ResolverActivity.ENABLE_TABBED_VIEW = true; List<ResolvedComponentInfo> personalResolvedComponentInfos = - createResolvedComponentsForTest(3); + createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), @@ -451,6 +451,11 @@ public class ResolverActivityTest { when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos); + when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class), + eq(UserHandle.SYSTEM))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendImageIntent(); markWorkProfileUserAvailable(); @@ -478,17 +483,20 @@ public class ResolverActivityTest { Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class), - eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); + eq(sOverrides.workProfileUserHandle))) + .thenReturn(new ArrayList<>(workResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class), - eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); + eq(sOverrides.workProfileUserHandle))) + .thenReturn(new ArrayList<>(workResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(new ArrayList<>(personalResolvedComponentInfos)); + Mockito.isA(List.class))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class), @@ -502,7 +510,7 @@ public class ResolverActivityTest { onView(withText(R.string.resolver_work_tab)).perform(click()); assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10)); - assertThat(activity.getPersonalListAdapter().getCount(), is(3)); + assertThat(activity.getPersonalListAdapter().getCount(), is(2)); } @Test @@ -511,14 +519,20 @@ public class ResolverActivityTest { ResolverActivity.ENABLE_TABBED_VIEW = true; markWorkProfileUserAvailable(); List<ResolvedComponentInfo> personalResolvedComponentInfos = - createResolvedComponentsForTest(3); + createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos); + Mockito.isA(List.class))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos); + when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class), + eq(UserHandle.SYSTEM))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendImageIntent(); final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); @@ -536,14 +550,20 @@ public class ResolverActivityTest { ResolverActivity.ENABLE_TABBED_VIEW = true; markWorkProfileUserAvailable(); List<ResolvedComponentInfo> personalResolvedComponentInfos = - createResolvedComponentsForTest(3); + createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos); + Mockito.isA(List.class))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos); + when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class), + eq(UserHandle.SYSTEM))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendImageIntent(); ResolveInfo[] chosen = new ResolveInfo[1]; sOverrides.onSafelyStartCallback = targetInfo -> { @@ -587,17 +607,20 @@ public class ResolverActivityTest { Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class), - eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); + eq(sOverrides.workProfileUserHandle))) + .thenReturn(new ArrayList<>(workResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class), - eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); + eq(sOverrides.workProfileUserHandle))) + .thenReturn(new ArrayList<>(workResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(new ArrayList<>(personalResolvedComponentInfos)); + Mockito.isA(List.class))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class), @@ -678,6 +701,20 @@ public class ResolverActivityTest { return infoList; } + private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile( + int numberOfResults, int userId) { + List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); + for (int i = 0; i < numberOfResults; i++) { + if (i == 0) { + infoList.add( + ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId)); + } else { + infoList.add(ResolverDataProvider.createResolvedComponentInfo(i)); + } + } + return infoList; + } + private void waitForIdle() { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml index a200a510cf12..fe1182ecad4f 100644 --- a/data/etc/com.android.settings.xml +++ b/data/etc/com.android.settings.xml @@ -26,6 +26,7 @@ <permission name="android.permission.DELETE_PACKAGES"/> <permission name="android.permission.FORCE_STOP_PACKAGES"/> <permission name="android.permission.LOCAL_MAC_ADDRESS"/> + <permission name="android.permission.LOG_COMPAT_CHANGE" /> <permission name="android.permission.MANAGE_DEBUGGING"/> <permission name="android.permission.MANAGE_DEVICE_ADMINS"/> <permission name="android.permission.MANAGE_FINGERPRINT"/> @@ -37,8 +38,10 @@ <permission name="android.permission.MODIFY_PHONE_STATE"/> <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <permission name="android.permission.MOVE_PACKAGE"/> + <permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" /> <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/> <permission name="android.permission.PACKAGE_USAGE_STATS"/> + <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.READ_SEARCH_INDEXABLES"/> <permission name="android.permission.REBOOT"/> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index b5eba090691f..b9a7e225bdb9 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -366,6 +366,10 @@ applications that come with the platform <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/> <!-- Permission required for Tethering CTS tests. --> <permission name="android.permission.TETHER_PRIVILEGED"/> + <!-- Permissions required for ganting and logging --> + <permission name="android.permission.LOG_COMPAT_CHANGE" /> + <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" /> <!-- Permissions required to test ambient display. --> <permission name="android.permission.READ_DREAM_STATE" /> <permission name="android.permission.WRITE_DREAM_STATE" /> diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 9c8345dafbc8..62124934f416 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -10,6 +10,7 @@ android_library { "androidx.appcompat_appcompat", "androidx.lifecycle_lifecycle-runtime", "androidx.mediarouter_mediarouter-nodeps", + "iconloader", "SettingsLibHelpUtils", "SettingsLibRestrictedLockUtils", diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index de523d9f9bc8..213e3657254d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -3,6 +3,7 @@ package com.android.settingslib; import android.annotation.ColorInt; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -13,6 +14,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.location.LocationManager; import android.media.AudioManager; @@ -27,8 +29,11 @@ import android.telephony.AccessNetworkConstants; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; +import androidx.annotation.NonNull; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.UserIcons; +import com.android.launcher3.icons.IconFactory; import com.android.settingslib.drawable.UserIconDrawable; import java.text.NumberFormat; @@ -424,6 +429,19 @@ public class Utils { return state; } + /** + * Get the {@link Drawable} that represents the app icon + */ + public static @NonNull Drawable getBadgedIcon( + @NonNull Context context, @NonNull ApplicationInfo appInfo) { + final UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid); + try (IconFactory iconFactory = IconFactory.obtain(context)) { + final Bitmap iconBmp = iconFactory.createBadgedIconBitmap( + appInfo.loadUnbadgedIcon(context.getPackageManager()), user, false).icon; + return new BitmapDrawable(context.getResources(), iconBmp); + } + } + private static boolean isNotInIwlan(ServiceState serviceState) { final NetworkRegistrationInfo networkRegWlan = serviceState.getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index 19c666459723..af728887c917 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -59,6 +59,7 @@ import androidx.lifecycle.OnLifecycleEvent; import com.android.internal.R; import com.android.internal.util.ArrayUtils; +import com.android.settingslib.Utils; import java.io.File; import java.io.IOException; @@ -495,7 +496,7 @@ public class ApplicationsState { return; } synchronized (entry) { - entry.ensureIconLocked(mContext, mDrawableFactory); + entry.ensureIconLocked(mContext); } } @@ -1216,7 +1217,7 @@ public class ApplicationsState { AppEntry entry = mAppEntries.get(i); if (entry.icon == null || !entry.mounted) { synchronized (entry) { - if (entry.ensureIconLocked(mContext, mDrawableFactory)) { + if (entry.ensureIconLocked(mContext)) { if (!mRunning) { mRunning = true; Message m = mMainHandler.obtainMessage( @@ -1587,10 +1588,10 @@ public class ApplicationsState { } } - boolean ensureIconLocked(Context context, IconDrawableFactory drawableFactory) { + boolean ensureIconLocked(Context context) { if (this.icon == null) { if (this.apkFile.exists()) { - this.icon = drawableFactory.getBadgedIcon(info); + this.icon = Utils.getBadgedIcon(context, info); return true; } else { this.mounted = false; @@ -1601,7 +1602,7 @@ public class ApplicationsState { // its icon. if (this.apkFile.exists()) { this.mounted = true; - this.icon = drawableFactory.getBadgedIcon(info); + this.icon = Utils.getBadgedIcon(context, info); return true; } } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 6a89b71be897..1d679c7bcbdd 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -223,6 +223,11 @@ <!-- permissions required for CTS test - PhoneStateListenerTest --> <uses-permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" /> + <!-- Permissions required for ganting and logging --> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> + <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/> + <!-- Permission required for CTS test - UiModeManagerTest --> <uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 139a8c3f7411..1fe967b4750d 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -645,7 +645,6 @@ <activity android:name=".controls.management.ControlsProviderSelectorActivity" android:label="Controls Providers" android:theme="@style/Theme.ControlsManagement" - android:exported="true" android:showForAllUsers="true" android:excludeFromRecents="true" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index a6f1d84877c5..7de1557ebc65 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -19,9 +19,12 @@ package com.android.systemui.controls.controller import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.ComponentName +import android.content.ContentResolver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.database.ContentObserver +import android.net.Uri import android.os.Environment import android.os.UserHandle import android.provider.Settings @@ -30,6 +33,7 @@ import android.service.controls.actions.ControlAction import android.util.ArrayMap import android.util.Log import com.android.internal.annotations.GuardedBy +import com.android.internal.annotations.VisibleForTesting import com.android.systemui.DumpController import com.android.systemui.Dumpable import com.android.systemui.broadcast.BroadcastDispatcher @@ -53,15 +57,16 @@ class ControlsControllerImpl @Inject constructor ( private val uiController: ControlsUiController, private val bindingController: ControlsBindingController, private val listingController: ControlsListingController, - broadcastDispatcher: BroadcastDispatcher, + private val broadcastDispatcher: BroadcastDispatcher, optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>, dumpController: DumpController ) : Dumpable, ControlsController { companion object { private const val TAG = "ControlsControllerImpl" - const val CONTROLS_AVAILABLE = "systemui.controls_available" - const val USER_CHANGE_RETRY_DELAY = 500L // ms + internal const val CONTROLS_AVAILABLE = "systemui.controls_available" + internal val URI = Settings.Secure.getUriFor(CONTROLS_AVAILABLE) + private const val USER_CHANGE_RETRY_DELAY = 500L // ms } // Map of map: ComponentName -> (String -> ControlInfo). @@ -69,9 +74,11 @@ class ControlsControllerImpl @Inject constructor ( @GuardedBy("currentFavorites") private val currentFavorites = ArrayMap<ComponentName, MutableMap<String, ControlInfo>>() - private var userChanging = true - override var available = Settings.Secure.getInt( - context.contentResolver, CONTROLS_AVAILABLE, 0) != 0 + private var userChanging: Boolean = true + + private val contentResolver: ContentResolver + get() = context.contentResolver + override var available = Settings.Secure.getInt(contentResolver, CONTROLS_AVAILABLE, 0) != 0 private set private var currentUser = context.user @@ -95,8 +102,8 @@ class ControlsControllerImpl @Inject constructor ( val fileName = Environment.buildPath( userContext.filesDir, ControlsFavoritePersistenceWrapper.FILE_NAME) persistenceWrapper.changeFile(fileName) - available = Settings.Secure.getIntForUser( - context.contentResolver, CONTROLS_AVAILABLE, 0) != 0 + available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE, + /* default */ 0, newUser.identifier) != 0 synchronized(currentFavorites) { currentFavorites.clear() } @@ -123,6 +130,25 @@ class ControlsControllerImpl @Inject constructor ( } } + @VisibleForTesting + internal val settingObserver = object : ContentObserver(null) { + override fun onChange(selfChange: Boolean, uri: Uri, userId: Int) { + // Do not listen to changes in the middle of user change, those will be read by the + // user-switch receiver. + if (userChanging || userId != currentUserId) { + return + } + available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE, + /* default */ 0, currentUserId) != 0 + synchronized(currentFavorites) { + currentFavorites.clear() + } + if (available) { + loadFavorites() + } + } + } + init { dumpController.registerDumpable(this) if (available) { @@ -135,6 +161,7 @@ class ControlsControllerImpl @Inject constructor ( executor, UserHandle.ALL ) + contentResolver.registerContentObserver(URI, false, settingObserver, UserHandle.USER_ALL) } private fun confirmAvailability(): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index fad2d94d6cf3..88b19b58a453 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -34,14 +34,17 @@ import android.widget.ImageView import android.widget.TextView import com.android.systemui.controls.controller.ControlsController +import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.R const val MIN_LEVEL = 0 const val MAX_LEVEL = 10000 +private const val UPDATE_DELAY_IN_MILLIS = 2000L class ControlViewHolder( val layout: ViewGroup, - val controlsController: ControlsController + val controlsController: ControlsController, + val uiExecutor: DelayableExecutor ) { val icon: ImageView = layout.requireViewById(R.id.icon) val status: TextView = layout.requireViewById(R.id.status) @@ -52,6 +55,7 @@ class ControlViewHolder( val clipLayer: ClipDrawable val gd: GradientDrawable lateinit var cws: ControlWithState + var cancelUpdate: Runnable? = null init { val ld = layout.getBackground() as LayerDrawable @@ -63,6 +67,8 @@ class ControlViewHolder( fun bindData(cws: ControlWithState) { this.cws = cws + cancelUpdate?.run() + val (status, template) = cws.control?.let { title.setText(it.getTitle()) subtitle.setText(it.getSubtitle()) @@ -86,6 +92,27 @@ class ControlViewHolder( findBehavior(status, template).apply(this, cws) } + fun actionResponse(@ControlAction.ResponseResult response: Int) { + val text = when (response) { + ControlAction.RESPONSE_OK -> "Success" + ControlAction.RESPONSE_FAIL -> "Error" + else -> "" + } + + if (!text.isEmpty()) { + val previousText = status.getText() + val previousTextExtra = statusExtra.getText() + + cancelUpdate = uiExecutor.executeDelayed({ + status.setText(previousText) + statusExtra.setText(previousTextExtra) + }, UPDATE_DELAY_IN_MILLIS) + + status.setText(text) + statusExtra.setText("") + } + } + fun action(action: ControlAction) { controlsController.action(cws.ci, action) } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt index b07a75d5e757..d70c86fc3266 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt @@ -22,6 +22,8 @@ import android.service.controls.actions.ControlAction import android.view.ViewGroup interface ControlsUiController { + val available: Boolean + fun show(parent: ViewGroup) fun hide() fun onRefreshState(componentName: ComponentName, controls: List<Control>) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index a777faf57fce..ed521e3be535 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -38,10 +38,10 @@ import com.android.systemui.controls.controller.ControlInfo import com.android.systemui.controls.management.ControlsProviderSelectorActivity import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.R +import com.android.systemui.util.concurrency.DelayableExecutor import dagger.Lazy -import java.util.concurrent.Executor import javax.inject.Inject import javax.inject.Singleton @@ -104,18 +104,23 @@ class TokenProviderConnection(val cc: ControlsController, val context: Context) } } +private data class ControlKey(val componentName: ComponentName, val controlId: String) + @Singleton class ControlsUiControllerImpl @Inject constructor ( val controlsController: Lazy<ControlsController>, val context: Context, - @Main val uiExecutor: Executor + @Main val uiExecutor: DelayableExecutor ) : ControlsUiController { private lateinit var controlInfos: List<ControlInfo> - private val controlsById = mutableMapOf<Pair<ComponentName, String>, ControlWithState>() - private val controlViewsById = mutableMapOf<String, ControlViewHolder>() + private val controlsById = mutableMapOf<ControlKey, ControlWithState>() + private val controlViewsById = mutableMapOf<ControlKey, ControlViewHolder>() private lateinit var parent: ViewGroup + override val available: Boolean + get() = controlsController.get().available + override fun show(parent: ViewGroup) { Log.d(TAG, "show()") @@ -125,7 +130,7 @@ class ControlsUiControllerImpl @Inject constructor ( controlInfos.map { ControlWithState(it, null) - }.associateByTo(controlsById) { Pair(it.ci.component, it.ci.controlId) } + }.associateByTo(controlsById) { ControlKey(it.ci.component, it.ci.controlId) } if (controlInfos.isEmpty()) { showInitialSetupView() @@ -178,9 +183,10 @@ class ControlsUiControllerImpl @Inject constructor ( val item = inflater.inflate( R.layout.controls_base_item, lastRow, false) as ViewGroup lastRow.addView(item) - val cvh = ControlViewHolder(item, controlsController.get()) - cvh.bindData(controlsById.get(Pair(it.component, it.controlId))!!) - controlViewsById.put(it.controlId, cvh) + val cvh = ControlViewHolder(item, controlsController.get(), uiExecutor) + val key = ControlKey(it.component, it.controlId) + cvh.bindData(controlsById.getValue(key)) + controlViewsById.put(key, cvh) } if ((controlInfos.size % 2) == 1) { @@ -205,21 +211,24 @@ class ControlsUiControllerImpl @Inject constructor ( override fun onRefreshState(componentName: ComponentName, controls: List<Control>) { Log.d(TAG, "onRefreshState()") controls.forEach { c -> - controlsById.get(Pair(componentName, c.getControlId()))?.let { + controlsById.get(ControlKey(componentName, c.getControlId()))?.let { Log.d(TAG, "onRefreshState() for id: " + c.getControlId()) val cws = ControlWithState(it.ci, c) - controlsById.put(Pair(componentName, c.getControlId()), cws) + val key = ControlKey(componentName, c.getControlId()) + controlsById.put(key, cws) uiExecutor.execute { - controlViewsById.get(c.getControlId())?.bindData(cws) + controlViewsById.get(key)?.bindData(cws) } } } } override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) { - Log.d(TAG, "onActionResponse()") - TODO("not implemented") + val key = ControlKey(componentName, controlId) + uiExecutor.execute { + controlViewsById.get(key)?.actionResponse(response) + } } private fun createRow(inflater: LayoutInflater, parent: ViewGroup): ViewGroup { diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 45c07a3e4693..082b0656b32e 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -1899,9 +1899,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } private boolean shouldShowControls() { - return isCurrentUserOwner() - && !mKeyguardManager.isDeviceLocked() - && Settings.Secure.getInt(mContext.getContentResolver(), - "systemui.controls_available", 0) == 1; + return !mKeyguardManager.isDeviceLocked() + && mControlsUiController.getAvailable(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index 0134aa3a15df..5de6d1c42b4f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -169,7 +169,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) { v.setText(mContext.getString( com.android.internal.R.string.bugreport_status, - Build.VERSION.RELEASE, + Build.VERSION.RELEASE_OR_CODENAME, Build.ID)); v.setVisibility(View.VISIBLE); } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java index 9e3e94ce4186..6c697184f082 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java @@ -36,6 +36,7 @@ import android.media.MediaMetadata; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.PlaybackState; +import android.os.Handler; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -370,6 +371,13 @@ public class QSMediaPlayer { if (mSeamless == null) { return; } + Handler handler = mSeamless.getHandler(); + handler.post(() -> { + updateChipInternal(device); + }); + } + + private void updateChipInternal(MediaDevice device) { ColorStateList fgTintList = ColorStateList.valueOf(mForegroundColor); // Update the outline color diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 2907cd41a0db..8dfcb0ac215d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -134,6 +134,8 @@ public class UserSwitcherController implements Dumpable { mBroadcastDispatcher.registerReceiver( mReceiver, filter, null /* handler */, UserHandle.SYSTEM); + mSimpleUserSwitcher = shouldUseSimpleUserSwitcher(); + mSecondaryUserServiceIntent = new Intent(context, SystemUISecondaryUserService.class); filter = new IntentFilter(); @@ -258,22 +260,20 @@ public class UserSwitcherController implements Dumpable { && mUserManager.canAddMoreUsers(); boolean createIsRestricted = !addUsersWhenLocked; - if (!mSimpleUserSwitcher) { - if (guestRecord == null) { - if (canCreateGuest) { - guestRecord = new UserRecord(null /* info */, null /* picture */, - true /* isGuest */, false /* isCurrent */, - false /* isAddUser */, createIsRestricted, canSwitchUsers); - checkIfAddUserDisallowedByAdminOnly(guestRecord); - records.add(guestRecord); - } - } else { - int index = guestRecord.isCurrent ? 0 : records.size(); - records.add(index, guestRecord); + if (guestRecord == null) { + if (canCreateGuest) { + guestRecord = new UserRecord(null /* info */, null /* picture */, + true /* isGuest */, false /* isCurrent */, + false /* isAddUser */, createIsRestricted, canSwitchUsers); + checkIfAddUserDisallowedByAdminOnly(guestRecord); + records.add(guestRecord); } + } else { + int index = guestRecord.isCurrent ? 0 : records.size(); + records.add(index, guestRecord); } - if (!mSimpleUserSwitcher && canCreateUser) { + if (canCreateUser) { UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */, false /* isGuest */, false /* isCurrent */, true /* isAddUser */, createIsRestricted, canSwitchUsers); @@ -562,8 +562,7 @@ public class UserSwitcherController implements Dumpable { private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { public void onChange(boolean selfChange) { - mSimpleUserSwitcher = Settings.Global.getInt(mContext.getContentResolver(), - SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0; + mSimpleUserSwitcher = shouldUseSimpleUserSwitcher(); mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0; refreshUsers(UserHandle.USER_NULL); @@ -579,6 +578,7 @@ public class UserSwitcherController implements Dumpable { final UserRecord u = mUsers.get(i); pw.print(" "); pw.println(u.toString()); } + pw.println("mSimpleUserSwitcher=" + mSimpleUserSwitcher); } public String getCurrentUserName(Context context) { @@ -717,6 +717,13 @@ public class UserSwitcherController implements Dumpable { } } + private boolean shouldUseSimpleUserSwitcher() { + int defaultSimpleUserSwitcher = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_expandLockScreenUserSwitcher) ? 1 : 0; + return Settings.Global.getInt(mContext.getContentResolver(), + SIMPLE_USER_SWITCHER_GLOBAL_SETTING, defaultSimpleUserSwitcher) != 0; + } + public void startActivity(Intent intent) { mActivityStarter.startActivity(intent, true); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java index a60ca6201419..49ada1a5e41e 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java @@ -158,7 +158,7 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference String demoTime = "1010"; // 10:10, a classic choice of horologists try { - String[] versionParts = android.os.Build.VERSION.RELEASE.split("\\."); + String[] versionParts = android.os.Build.VERSION.RELEASE_OR_CODENAME.split("\\."); int majorVersion = Integer.valueOf(versionParts[0]); demoTime = String.format("%02d00", majorVersion % 24); } catch (IllegalArgumentException ex) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt index 897091f69f36..e3bcdc8b0b60 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt @@ -32,12 +32,13 @@ import androidx.test.filters.SmallTest import com.android.systemui.DumpController import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher -import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ControlStatus +import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -82,7 +83,7 @@ class ControlsControllerImplTest : SysuiTestCase() { private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver> private lateinit var delayableExecutor: FakeExecutor - private lateinit var controller: ControlsController + private lateinit var controller: ControlsControllerImpl companion object { fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() @@ -416,5 +417,70 @@ class ControlsControllerImplTest : SysuiTestCase() { verify(listingController).changeUser(UserHandle.of(otherUser)) assertTrue(controller.getFavoriteControls().isEmpty()) assertEquals(otherUser, controller.currentUserId) + assertTrue(controller.available) + } + + @Test + fun testDisableFeature_notAvailable() { + Settings.Secure.putIntForUser(mContext.contentResolver, + ControlsControllerImpl.CONTROLS_AVAILABLE, 0, user) + controller.settingObserver.onChange(false, ControlsControllerImpl.URI, 0) + assertFalse(controller.available) + } + + @Test + fun testDisableFeature_clearFavorites() { + controller.changeFavoriteStatus(TEST_CONTROL_INFO, true) + assertFalse(controller.getFavoriteControls().isEmpty()) + + Settings.Secure.putIntForUser(mContext.contentResolver, + ControlsControllerImpl.CONTROLS_AVAILABLE, 0, user) + controller.settingObserver.onChange(false, ControlsControllerImpl.URI, user) + assertTrue(controller.getFavoriteControls().isEmpty()) + } + + @Test + fun testDisableFeature_noChangeForNotCurrentUser() { + controller.changeFavoriteStatus(TEST_CONTROL_INFO, true) + Settings.Secure.putIntForUser(mContext.contentResolver, + ControlsControllerImpl.CONTROLS_AVAILABLE, 0, otherUser) + controller.settingObserver.onChange(false, ControlsControllerImpl.URI, otherUser) + + assertTrue(controller.available) + assertFalse(controller.getFavoriteControls().isEmpty()) + } + + @Test + fun testCorrectUserSettingOnUserChange() { + Settings.Secure.putIntForUser(mContext.contentResolver, + ControlsControllerImpl.CONTROLS_AVAILABLE, 0, otherUser) + + val intent = Intent(Intent.ACTION_USER_SWITCHED).apply { + putExtra(Intent.EXTRA_USER_HANDLE, otherUser) + } + val pendingResult = mock(BroadcastReceiver.PendingResult::class.java) + `when`(pendingResult.sendingUserId).thenReturn(otherUser) + broadcastReceiverCaptor.value.pendingResult = pendingResult + + broadcastReceiverCaptor.value.onReceive(mContext, intent) + + assertFalse(controller.available) + } + + @Test + fun testCountFavoritesForComponent_singleComponent() { + controller.changeFavoriteStatus(TEST_CONTROL_INFO, true) + + assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT)) + assertEquals(0, controller.countFavoritesForComponent(TEST_COMPONENT_2)) + } + + @Test + fun testCountFavoritesForComponent_multipleComponents() { + controller.changeFavoriteStatus(TEST_CONTROL_INFO, true) + controller.changeFavoriteStatus(TEST_CONTROL_INFO_2, true) + + assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT)) + assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT_2)) } -}
\ No newline at end of file +} diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 07abe1adeb52..39c402be84a3 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -272,13 +272,6 @@ public class Tethering { mStateReceiver = new StateReceiver(); - mNetdCallback = new NetdCallback(); - try { - mNetd.registerUnsolicitedEventListener(mNetdCallback); - } catch (RemoteException e) { - mLog.e("Unable to register netd UnsolicitedEventListener"); - } - final UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); mTetheringRestriction = new UserRestrictionActionListener(userManager, this); @@ -287,6 +280,14 @@ public class Tethering { // Load tethering configuration. updateConfiguration(); + // NetdCallback should be registered after updateConfiguration() to ensure + // TetheringConfiguration is created. + mNetdCallback = new NetdCallback(); + try { + mNetd.registerUnsolicitedEventListener(mNetdCallback); + } catch (RemoteException e) { + mLog.e("Unable to register netd UnsolicitedEventListener"); + } startStateMachineUpdaters(mHandler); startTrackDefaultNetwork(); diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 2fc9d04623f8..c4e6427a0dfb 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -16,6 +16,11 @@ package com.android.server.compat; +import static android.Manifest.permission.LOG_COMPAT_CHANGE; +import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG; +import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + import android.app.ActivityManager; import android.app.IActivityManager; import android.content.Context; @@ -67,12 +72,14 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void reportChange(long changeId, ApplicationInfo appInfo) { + checkCompatChangeLogPermission(); reportChange(changeId, appInfo.uid, ChangeReporter.STATE_LOGGED); } @Override public void reportChangeByPackageName(long changeId, String packageName, int userId) { + checkCompatChangeLogPermission(); ApplicationInfo appInfo = getApplicationInfo(packageName, userId); if (appInfo == null) { return; @@ -82,11 +89,13 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void reportChangeByUid(long changeId, int uid) { + checkCompatChangeLogPermission(); reportChange(changeId, uid, ChangeReporter.STATE_LOGGED); } @Override public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) { + checkCompatChangeReadAndLogPermission(); if (mCompatConfig.isChangeEnabled(changeId, appInfo)) { reportChange(changeId, appInfo.uid, ChangeReporter.STATE_ENABLED); @@ -99,6 +108,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) { + checkCompatChangeReadAndLogPermission(); ApplicationInfo appInfo = getApplicationInfo(packageName, userId); if (appInfo == null) { return true; @@ -108,6 +118,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public boolean isChangeEnabledByUid(long changeId, int uid) { + checkCompatChangeReadAndLogPermission(); String[] packages = mContext.getPackageManager().getPackagesForUid(uid); if (packages == null || packages.length == 0) { return true; @@ -140,6 +151,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void setOverrides(CompatibilityChangeConfig overrides, String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); mCompatConfig.addOverrides(overrides, packageName); killPackage(packageName); } @@ -147,11 +159,13 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); mCompatConfig.addOverrides(overrides, packageName); } @Override public void clearOverrides(String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); mCompatConfig.removePackageOverrides(packageName); killPackage(packageName); } @@ -159,12 +173,14 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void clearOverridesForTest(String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); mCompatConfig.removePackageOverrides(packageName); } @Override public boolean clearOverride(long changeId, String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); boolean existed = mCompatConfig.removeOverride(changeId, packageName); killPackage(packageName); return existed; @@ -172,11 +188,13 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) { + checkCompatChangeReadAndLogPermission(); return mCompatConfig.getAppConfig(appInfo); } @Override public CompatibilityChangeInfo[] listAllChanges() { + checkCompatChangeReadPermission(); return mCompatConfig.dumpChanges(); } @@ -215,6 +233,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + checkCompatChangeReadAndLogPermission(); if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return; mCompatConfig.dumpConfig(pw); } @@ -272,4 +291,30 @@ public class PlatformCompat extends IPlatformCompat.Stub { Binder.restoreCallingIdentity(identity); } } + + private void checkCompatChangeLogPermission() throws SecurityException { + if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE) + != PERMISSION_GRANTED) { + throw new SecurityException("Cannot log compat change usage"); + } + } + + private void checkCompatChangeReadPermission() throws SecurityException { + if (mContext.checkCallingOrSelfPermission(READ_COMPAT_CHANGE_CONFIG) + != PERMISSION_GRANTED) { + throw new SecurityException("Cannot read compat change"); + } + } + + private void checkCompatChangeOverridePermission() throws SecurityException { + if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG) + != PERMISSION_GRANTED) { + throw new SecurityException("Cannot override compat change"); + } + } + + private void checkCompatChangeReadAndLogPermission() throws SecurityException { + checkCompatChangeReadPermission(); + checkCompatChangeLogPermission(); + } } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 40ea6cfb6d4a..b98bb0831b0e 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -234,12 +234,12 @@ public class Installer extends SystemService { } public void moveCompleteApp(String fromUuid, String toUuid, String packageName, - String dataAppName, int appId, String seInfo, int targetSdkVersion) - throws InstallerException { + String dataAppName, int appId, String seInfo, int targetSdkVersion, + String fromCodePath) throws InstallerException { if (!checkBeforeRemote()) return; try { mInstalld.moveCompleteApp(fromUuid, toUuid, packageName, dataAppName, appId, seInfo, - targetSdkVersion); + targetSdkVersion, fromCodePath); } catch (Exception e) { throw InstallerException.from(e); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c14b42dd69c8..c85859072d89 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -150,6 +150,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.AuxiliaryResolveInfo; import android.content.pm.ChangedPackages; import android.content.pm.ComponentInfo; +import android.content.pm.DataLoaderType; import android.content.pm.FallbackCategoryProvider; import android.content.pm.FeatureInfo; import android.content.pm.IDexModuleRegisterCallback; @@ -1658,7 +1659,8 @@ public class PackageManagerService extends IPackageManager.Stub handlePackagePostInstall(parentRes, grantPermissions, killApp, virtualPreload, grantedPermissions, whitelistedRestrictedPermissions, didRestore, - args.installSource.installerPackageName, args.observer); + args.installSource.installerPackageName, args.observer, + args.mDataLoaderType); // Handle the child packages final int childCount = (parentRes.addedChildPackages != null) @@ -1668,7 +1670,8 @@ public class PackageManagerService extends IPackageManager.Stub handlePackagePostInstall(childRes, grantPermissions, killApp, virtualPreload, grantedPermissions, whitelistedRestrictedPermissions, false /*didRestore*/, - args.installSource.installerPackageName, args.observer); + args.installSource.installerPackageName, args.observer, + args.mDataLoaderType); } // Log tracing if needed @@ -1995,7 +1998,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean killApp, boolean virtualPreload, String[] grantedPermissions, List<String> whitelistedRestrictedPermissions, boolean launchedForRestore, String installerPackage, - IPackageInstallObserver2 installObserver) { + IPackageInstallObserver2 installObserver, int dataLoaderType) { final boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED; final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null; @@ -2096,11 +2099,14 @@ public class PackageManagerService extends IPackageManager.Stub if (update) { extras.putBoolean(Intent.EXTRA_REPLACING, true); } + extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType); + // Send to all running apps. sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, null /*targetPackage*/, null /*finishedReceiver*/, updateUserIds, instantUserIds); if (installerPackageName != null) { + // Send to the installer, even if it's not running. sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, installerPackageName, null /*finishedReceiver*/, @@ -13978,9 +13984,11 @@ public class PackageManagerService extends IPackageManager.Stub final int appId; final String seinfo; final int targetSdkVersion; + final String fromCodePath; public MoveInfo(int moveId, String fromUuid, String toUuid, String packageName, - String dataAppName, int appId, String seinfo, int targetSdkVersion) { + String dataAppName, int appId, String seinfo, int targetSdkVersion, + String fromCodePath) { this.moveId = moveId; this.fromUuid = fromUuid; this.toUuid = toUuid; @@ -13989,6 +13997,7 @@ public class PackageManagerService extends IPackageManager.Stub this.appId = appId; this.seinfo = seinfo; this.targetSdkVersion = targetSdkVersion; + this.fromCodePath = fromCodePath; } } @@ -14114,13 +14123,14 @@ public class PackageManagerService extends IPackageManager.Stub MultiPackageInstallParams mParentInstallParams; final long requiredInstalledVersionCode; final boolean forceQueryableOverride; + final int mDataLoaderType; InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer, int installFlags, InstallSource installSource, String volumeUuid, VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride, String[] grantedPermissions, List<String> whitelistedRestrictedPermissions, SigningDetails signingDetails, int installReason, - long requiredInstalledVersionCode) { + long requiredInstalledVersionCode, int dataLoaderType) { super(user); this.origin = origin; this.move = move; @@ -14136,40 +14146,42 @@ public class PackageManagerService extends IPackageManager.Stub this.installReason = installReason; this.requiredInstalledVersionCode = requiredInstalledVersionCode; this.forceQueryableOverride = false; + this.mDataLoaderType = dataLoaderType; } InstallParams(ActiveInstallSession activeInstallSession) { super(activeInstallSession.getUser()); + final PackageInstaller.SessionParams sessionParams = + activeInstallSession.getSessionParams(); if (DEBUG_INSTANT) { - if ((activeInstallSession.getSessionParams().installFlags + if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) { Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName()); } } verificationInfo = new VerificationInfo( - activeInstallSession.getSessionParams().originatingUri, - activeInstallSession.getSessionParams().referrerUri, - activeInstallSession.getSessionParams().originatingUid, + sessionParams.originatingUri, + sessionParams.referrerUri, + sessionParams.originatingUid, activeInstallSession.getInstallerUid()); origin = OriginInfo.fromStagedFile(activeInstallSession.getStagedDir()); move = null; installReason = fixUpInstallReason( activeInstallSession.getInstallSource().installerPackageName, activeInstallSession.getInstallerUid(), - activeInstallSession.getSessionParams().installReason); + sessionParams.installReason); observer = activeInstallSession.getObserver(); - installFlags = activeInstallSession.getSessionParams().installFlags; + installFlags = sessionParams.installFlags; installSource = activeInstallSession.getInstallSource(); - volumeUuid = activeInstallSession.getSessionParams().volumeUuid; - packageAbiOverride = activeInstallSession.getSessionParams().abiOverride; - grantedRuntimePermissions = activeInstallSession.getSessionParams() - .grantedRuntimePermissions; - whitelistedRestrictedPermissions = activeInstallSession.getSessionParams() - .whitelistedRestrictedPermissions; + volumeUuid = sessionParams.volumeUuid; + packageAbiOverride = sessionParams.abiOverride; + grantedRuntimePermissions = sessionParams.grantedRuntimePermissions; + whitelistedRestrictedPermissions = sessionParams.whitelistedRestrictedPermissions; signingDetails = activeInstallSession.getSigningDetails(); - requiredInstalledVersionCode = activeInstallSession.getSessionParams() - .requiredInstalledVersionCode; - forceQueryableOverride = activeInstallSession.getSessionParams().forceQueryableOverride; + requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode; + forceQueryableOverride = sessionParams.forceQueryableOverride; + mDataLoaderType = (sessionParams.dataLoaderParams != null) + ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE; } @Override @@ -14772,6 +14784,7 @@ public class PackageManagerService extends IPackageManager.Stub final int installReason; final boolean forceQueryableOverride; @Nullable final MultiPackageInstallParams mMultiPackageInstallParams; + final int mDataLoaderType; // The list of instruction sets supported by this app. This is currently // only used during the rmdex() phase to clean up resources. We can get rid of this @@ -14785,7 +14798,7 @@ public class PackageManagerService extends IPackageManager.Stub List<String> whitelistedRestrictedPermissions, String traceMethod, int traceCookie, SigningDetails signingDetails, int installReason, boolean forceQueryableOverride, - MultiPackageInstallParams multiPackageInstallParams) { + MultiPackageInstallParams multiPackageInstallParams, int dataLoaderType) { this.origin = origin; this.move = move; this.installFlags = installFlags; @@ -14803,6 +14816,7 @@ public class PackageManagerService extends IPackageManager.Stub this.installReason = installReason; this.forceQueryableOverride = forceQueryableOverride; this.mMultiPackageInstallParams = multiPackageInstallParams; + this.mDataLoaderType = dataLoaderType; } /** New install */ @@ -14812,7 +14826,8 @@ public class PackageManagerService extends IPackageManager.Stub params.getUser(), null /*instructionSets*/, params.packageAbiOverride, params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions, params.traceMethod, params.traceCookie, params.signingDetails, - params.installReason, params.forceQueryableOverride, params.mParentInstallParams); + params.installReason, params.forceQueryableOverride, + params.mParentInstallParams, params.mDataLoaderType); } abstract int copyApk(); @@ -14903,7 +14918,8 @@ public class PackageManagerService extends IPackageManager.Stub super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY, null, null, instructionSets, null, null, null, null, 0, PackageParser.SigningDetails.UNKNOWN, - PackageManager.INSTALL_REASON_UNKNOWN, false, null /* parent */); + PackageManager.INSTALL_REASON_UNKNOWN, false, null /* parent */, + DataLoaderType.NONE); this.codeFile = (codePath != null) ? new File(codePath) : null; this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null; } @@ -14983,9 +14999,7 @@ public class PackageManagerService extends IPackageManager.Stub try { makeDirRecursive(afterCodeFile.getParentFile(), 0775); if (onIncremental) { - // TODO(b/147371381): fix incremental installation - mIncrementalManager.rename(beforeCodeFile.getAbsolutePath(), - afterCodeFile.getAbsolutePath()); + mIncrementalManager.renameCodePath(beforeCodeFile, afterCodeFile); } else { Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath()); } @@ -15104,7 +15118,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mInstaller) { try { mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName, - move.dataAppName, move.appId, move.seinfo, move.targetSdkVersion); + move.dataAppName, move.appId, move.seinfo, move.targetSdkVersion, + move.fromCodePath); } catch (InstallerException e) { Slog.w(TAG, "Failed to move app", e); return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; @@ -22054,6 +22069,7 @@ public class PackageManagerService extends IPackageManager.Stub final PackageFreezer freezer; final int[] installedUserIds; final boolean isCurrentLocationExternal; + final String fromCodePath; // reader synchronized (mLock) { @@ -22110,6 +22126,7 @@ public class PackageManagerService extends IPackageManager.Stub targetSdkVersion = pkg.getTargetSdkVersion(); freezer = freezePackage(packageName, "movePackageInternal"); installedUserIds = ps.queryInstalledUsers(mUserManager.getUserIds(), true); + fromCodePath = pkg.getCodePath(); } final Bundle extras = new Bundle(); @@ -22238,7 +22255,7 @@ public class PackageManagerService extends IPackageManager.Stub final String dataAppName = codeFile.getName(); move = new MoveInfo(moveId, currentVolumeUuid, volumeUuid, packageName, - dataAppName, appId, seinfo, targetSdkVersion); + dataAppName, appId, seinfo, targetSdkVersion, fromCodePath); } else { move = null; } @@ -22251,7 +22268,8 @@ public class PackageManagerService extends IPackageManager.Stub installSource, volumeUuid, null /*verificationInfo*/, user, packageAbiOverride, null /*grantedPermissions*/, null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN, - PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST); + PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST, + DataLoaderType.NONE); params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params)); msg.obj = params; diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java index 6daf5162ebad..6eba59acbc94 100644 --- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java +++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java @@ -402,7 +402,7 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn public String getStatus() { return mContext.getString( com.android.internal.R.string.bugreport_status, - Build.VERSION.RELEASE, + Build.VERSION.RELEASE_OR_CODENAME, Build.ID); } } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 96f1219861ec..5c79f6e6391d 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -2480,7 +2480,7 @@ public class StatsPullAtomService extends SystemService { .writeString(Build.BRAND) .writeString(Build.PRODUCT) .writeString(Build.DEVICE) - .writeString(Build.VERSION.RELEASE) + .writeString(Build.VERSION.RELEASE_OR_CODENAME) .writeString(Build.ID) .writeString(Build.VERSION.INCREMENTAL) .writeString(Build.TYPE) diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 75d87edbc437..f35ba9e69ed7 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -386,6 +386,8 @@ public class ActivityStartController { } else { callingPid = callingUid = -1; } + final int filterCallingUid = ActivityStarter.computeResolveFilterUid( + callingUid, realCallingUid, UserHandle.USER_NULL); final SparseArray<String> startingUidPkgs = new SparseArray<>(); final long origId = Binder.clearCallingIdentity(); try { @@ -408,9 +410,7 @@ public class ActivityStartController { // Collect information about the target of the Intent. ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], - 0 /* startFlags */, null /* profilerInfo */, userId, - ActivityStarter.computeResolveFilterUid( - callingUid, realCallingUid, UserHandle.USER_NULL)); + 0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid); aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId); if (aInfo != null) { @@ -457,6 +457,7 @@ public class ActivityStartController { Slog.wtf(TAG, sb.toString()); } + final IBinder sourceResultTo = resultTo; final ActivityRecord[] outActivity = new ActivityRecord[1]; // Lock the loop to ensure the activities launched in a sequence. synchronized (mService.mGlobalLock) { @@ -470,7 +471,18 @@ public class ActivityStartController { } return startResult; } - resultTo = outActivity[0] != null ? outActivity[0].appToken : null; + final ActivityRecord started = outActivity[0]; + if (started != null && started.getUid() == filterCallingUid) { + // Only the started activity which has the same uid as the source caller can + // be the caller of next activity. + resultTo = started.appToken; + } else { + resultTo = sourceResultTo; + // Different apps not adjacent to the caller are forced to be new task. + if (i < starters.length - 1) { + starters[i + 1].getIntent().addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + } } } } finally { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 9e3292b59402..c7270f257923 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2493,7 +2493,6 @@ class ActivityStarter { return this; } - @VisibleForTesting Intent getIntent() { return mRequest.intent; } diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml index 0e24b0314b2d..44eb8285c7db 100644 --- a/services/tests/mockingservicestests/AndroidManifest.xml +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -17,6 +17,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.mockingservicestests"> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> <uses-permission android:name="android.permission.HARDWARE_TEST"/> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index d2ddff3627b9..1212f2082404 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -65,6 +65,8 @@ <uses-permission android:name="android.permission.WATCH_APPOPS" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.SUSPEND_APPS"/> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" /> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/> <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/> <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml index 180deb5c4dcc..dab0a5f0e279 100644 --- a/services/tests/uiservicestests/AndroidManifest.xml +++ b/services/tests/uiservicestests/AndroidManifest.xml @@ -28,6 +28,8 @@ <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" /> <uses-permission android:name="android.permission.DEVICE_POWER" /> <uses-permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY" /> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" /> <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/> diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp index 5e9ef8efc402..74dfde848191 100644 --- a/tests/PlatformCompatGating/Android.bp +++ b/tests/PlatformCompatGating/Android.bp @@ -18,14 +18,11 @@ android_test { name: "PlatformCompatGating", // Only compile source java files in this apk. srcs: ["src/**/*.java"], - certificate: "platform", - libs: [ - "android.test.runner", - "android.test.base", - ], static_libs: [ "junit", - "android-support-test", + "androidx.test.runner", + "androidx.test.core", + "androidx.test.ext.junit", "mockito-target-minus-junit4", "truth-prebuilt", "platform-compat-test-rules" diff --git a/tests/PlatformCompatGating/AndroidManifest.xml b/tests/PlatformCompatGating/AndroidManifest.xml index 7f14b83fbc75..c24dc31b7bf3 100644 --- a/tests/PlatformCompatGating/AndroidManifest.xml +++ b/tests/PlatformCompatGating/AndroidManifest.xml @@ -6,6 +6,6 @@ <uses-library android:name="android.test.runner" /> </application> - <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.tests.gating"/> </manifest> diff --git a/tests/PlatformCompatGating/AndroidTest.xml b/tests/PlatformCompatGating/AndroidTest.xml index c62684837332..0c7485b27fb8 100644 --- a/tests/PlatformCompatGating/AndroidTest.xml +++ b/tests/PlatformCompatGating/AndroidTest.xml @@ -24,7 +24,6 @@ <test class="com.android.tradefed.testtype.AndroidJUnitTest"> <option name="package" value="com.android.tests.gating"/> - <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> <option name="hidden-api-checks" value="false"/> </test> </configuration> diff --git a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java index dc317f1941c7..c1ce0e99640c 100644 --- a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java +++ b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java @@ -18,8 +18,9 @@ package com.android.tests.gating; import static com.google.common.truth.Truth.assertThat; import android.compat.testing.PlatformCompatChangeRule; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.compat.testing.DummyApi; @@ -81,14 +82,14 @@ public class PlatformCompatGatingTest { @Test @EnableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER}) public void testDummyGatingPositiveSystemServer() { - assertThat( - DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isTrue(); + assertThat(DummyApi.dummySystemServer( + InstrumentationRegistry.getInstrumentation().getTargetContext())).isTrue(); } @Test @DisableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER}) public void testDummyGatingNegativeSystemServer() { - assertThat( - DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isFalse(); + assertThat(DummyApi.dummySystemServer( + InstrumentationRegistry.getInstrumentation().getTargetContext())).isFalse(); } } diff --git a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatPermissionsTest.java b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatPermissionsTest.java new file mode 100644 index 000000000000..9b9e5815a588 --- /dev/null +++ b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatPermissionsTest.java @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tests.gating; + +import static android.Manifest.permission.LOG_COMPAT_CHANGE; +import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG; +import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; + +import android.app.Instrumentation; +import android.app.UiAutomation; +import android.compat.Compatibility.ChangeConfig; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Process; +import android.os.ServiceManager; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.internal.compat.CompatibilityChangeConfig; +import com.android.internal.compat.IPlatformCompat; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.HashSet; +import java.util.Set; + +@RunWith(JUnit4.class) +public final class PlatformCompatPermissionsTest { + + // private Context mContext; + private IPlatformCompat mPlatformCompat; + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + private Context mContext; + private UiAutomation mUiAutomation; + private PackageManager mPackageManager; + + @Before + public void setUp() { + // mContext; + mPlatformCompat = IPlatformCompat.Stub + .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + mUiAutomation = instrumentation.getUiAutomation(); + mContext = instrumentation.getTargetContext(); + + mPackageManager = mContext.getPackageManager(); + } + + @After + public void tearDown() { + + mUiAutomation.dropShellPermissionIdentity(); + } + + @Test + public void reportChange_noLogCompatChangePermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.reportChange(1, mPackageManager.getApplicationInfo(packageName, 0)); + } + + @Test + public void reportChange_logCompatChangePermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(LOG_COMPAT_CHANGE); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.reportChange(1, mPackageManager.getApplicationInfo(packageName, 0)); + } + + @Test + public void reportChangeByPackageName_noLogCompatChangePermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.reportChangeByPackageName(1, packageName, 0); + } + + @Test + public void reportChangeByPackageName_logCompatChangePermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(LOG_COMPAT_CHANGE); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.reportChangeByPackageName(1, packageName, 0); + } + + @Test + public void reportChangeByUid_noLogCompatChangePermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + + mPlatformCompat.reportChangeByUid(1, Process.myUid()); + } + + @Test + public void reportChangeByUid_logCompatChangePermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(LOG_COMPAT_CHANGE); + + mPlatformCompat.reportChangeByUid(1, Process.myUid()); + } + + @Test + public void isChangeEnabled_noReadCompatConfigPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.isChangeEnabled(1, mPackageManager.getApplicationInfo(packageName, 0)); + } + + @Test + public void isChangeEnabled_noLogCompatChangeConfigPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.isChangeEnabled(1, mPackageManager.getApplicationInfo(packageName, 0)); + } + + @Test + public void isChangeEnabled_readAndLogCompatChangeConfigPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.isChangeEnabled(1, mPackageManager.getApplicationInfo(packageName, 0)); + } + + @Test + public void isChangeEnabledByPackageName_noReadCompatConfigPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.isChangeEnabledByPackageName(1, packageName, 0); + } + + @Test + public void isChangeEnabledByPackageName_noLogompatConfigPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.isChangeEnabledByPackageName(1, packageName, 0); + } + + @Test + public void isChangeEnabledByPackageName_readAndLogCompatChangeConfigPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.isChangeEnabledByPackageName(1, packageName, 0); + } + + @Test + public void isChangeEnabledByUid_noReadCompatConfigPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + + mPlatformCompat.isChangeEnabledByUid(1, Process.myUid()); + } + + @Test + public void isChangeEnabledByUid_noLogCompatChangePermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG); + + mPlatformCompat.isChangeEnabledByUid(1, Process.myUid()); + } + + @Test + public void isChangeEnabledByUid_readAndLogCompatChangeConfigPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE); + + mPlatformCompat.isChangeEnabledByUid(1, Process.myUid()); + } + + @Test + public void setOverrides_noOverridesPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + Set<Long> enabled = new HashSet<>(); + Set<Long> disabled = new HashSet<>(); + ChangeConfig changeConfig = new ChangeConfig(enabled, disabled); + CompatibilityChangeConfig compatibilityChangeConfig = + new CompatibilityChangeConfig(changeConfig); + + mPlatformCompat.setOverrides(compatibilityChangeConfig, "foo.bar"); + } + @Test + public void setOverrides_overridesPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG); + Set<Long> enabled = new HashSet<>(); + Set<Long> disabled = new HashSet<>(); + ChangeConfig changeConfig = new ChangeConfig(enabled, disabled); + CompatibilityChangeConfig compatibilityChangeConfig = + new CompatibilityChangeConfig(changeConfig); + + mPlatformCompat.setOverrides(compatibilityChangeConfig, "foo.bar"); + } + + @Test + public void setOverridesForTest_noOverridesPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + Set<Long> enabled = new HashSet<>(); + Set<Long> disabled = new HashSet<>(); + ChangeConfig changeConfig = new ChangeConfig(enabled, disabled); + CompatibilityChangeConfig compatibilityChangeConfig = + new CompatibilityChangeConfig(changeConfig); + + mPlatformCompat.setOverridesForTest(compatibilityChangeConfig, "foo.bar"); + } + @Test + public void setOverridesForTest_overridesPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG); + Set<Long> enabled = new HashSet<>(); + Set<Long> disabled = new HashSet<>(); + ChangeConfig changeConfig = new ChangeConfig(enabled, disabled); + CompatibilityChangeConfig compatibilityChangeConfig = + new CompatibilityChangeConfig(changeConfig); + + mPlatformCompat.setOverridesForTest(compatibilityChangeConfig, "foo.bar"); + } + + @Test + public void clearOverrides_noOverridesPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + + mPlatformCompat.clearOverrides("foo.bar"); + } + @Test + public void clearOverrides_overridesPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG); + + mPlatformCompat.clearOverrides("foo.bar"); + } + + @Test + public void clearOverridesForTest_noOverridesPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + + mPlatformCompat.clearOverridesForTest("foo.bar"); + } + @Test + public void clearOverridesForTest_overridesPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG); + + mPlatformCompat.clearOverridesForTest("foo.bar"); + } + + @Test + public void clearOverride_noOverridesPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + + mPlatformCompat.clearOverride(1, "foo.bar"); + } + @Test + public void clearOverride_overridesPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG); + + mPlatformCompat.clearOverride(1, "foo.bar"); + } + + @Test + public void listAllChanges_noReadCompatConfigPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + + mPlatformCompat.listAllChanges(); + } + @Test + public void listAllChanges_readCompatConfigPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG); + + mPlatformCompat.listAllChanges(); + } +} diff --git a/tests/PlatformCompatGating/test-rules/Android.bp b/tests/PlatformCompatGating/test-rules/Android.bp index 8211ef523ee7..10fa2dc0d7c6 100644 --- a/tests/PlatformCompatGating/test-rules/Android.bp +++ b/tests/PlatformCompatGating/test-rules/Android.bp @@ -19,7 +19,7 @@ java_library { srcs: ["src/**/*.java"], static_libs: [ "junit", - "android-support-test", + "androidx.test.core", "truth-prebuilt", "core-compat-test-rules" ], diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java index 932ec643d478..d6846faa5c00 100644 --- a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java +++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java @@ -16,13 +16,17 @@ package android.compat.testing; +import android.Manifest; import android.app.Instrumentation; +import android.app.UiAutomation; import android.compat.Compatibility; import android.compat.Compatibility.ChangeConfig; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; -import android.support.test.InstrumentationRegistry; + +import androidx.test.platform.app.InstrumentationRegistry; + import com.android.internal.compat.CompatibilityChangeConfig; import com.android.internal.compat.IPlatformCompat; @@ -83,12 +87,17 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule { @Override public void evaluate() throws Throwable { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + UiAutomation uiAutomation = instrumentation.getUiAutomation(); String packageName = instrumentation.getTargetContext().getPackageName(); IPlatformCompat platformCompat = IPlatformCompat.Stub .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); if (platformCompat == null) { throw new IllegalStateException("Could not get IPlatformCompat service!"); } + uiAutomation.adoptShellPermissionIdentity( + Manifest.permission.LOG_COMPAT_CHANGE, + Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG, + Manifest.permission.READ_COMPAT_CHANGE_CONFIG); Compatibility.setOverrides(mConfig); try { platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig), @@ -101,6 +110,7 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule { } catch (RemoteException e) { throw new RuntimeException("Could not call IPlatformCompat binder method!", e); } finally { + uiAutomation.dropShellPermissionIdentity(); Compatibility.clearOverrides(); } } diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp index d3958a65c704..a251c053e004 100644 --- a/tools/stats_log_api_gen/Android.bp +++ b/tools/stats_log_api_gen/Android.bp @@ -30,7 +30,7 @@ cc_binary_host { "utils.cpp", ], cflags: [ - "-DSTATS_SCHEMA_LEGACY", + //"-DSTATS_SCHEMA_LEGACY", "-Wall", "-Werror", ], |