Merge "Comment out Statsd tests"
diff --git a/apex/blobstore/TEST_MAPPING b/apex/blobstore/TEST_MAPPING
index 25a1537..6d3c0d7 100644
--- a/apex/blobstore/TEST_MAPPING
+++ b/apex/blobstore/TEST_MAPPING
@@ -4,6 +4,9 @@
"name": "CtsBlobStoreTestCases"
},
{
+ "name": "CtsBlobStoreHostTestCases"
+ },
+ {
"name": "FrameworksMockingServicesTests",
"options": [
{
@@ -12,4 +15,4 @@
]
}
]
-}
\ No newline at end of file
+}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 80b4235..d33a09f 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -514,7 +514,7 @@
static BlobStoreSession createFromXml(@NonNull XmlPullParser in, int version,
@NonNull Context context, @NonNull SessionStateChangeListener stateChangeListener)
throws IOException, XmlPullParserException {
- final int sessionId = XmlUtils.readIntAttribute(in, ATTR_ID);
+ final long sessionId = XmlUtils.readLongAttribute(in, ATTR_ID);
final String ownerPackageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
final int ownerUid = XmlUtils.readIntAttribute(in, ATTR_UID);
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index c8aa526..0e93110 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -27,6 +27,7 @@
"framework-statsd",
"service-statsd",
],
+ compile_multilib: "both",
// prebuilts: ["my_prebuilt"],
name: "com.android.os.statsd-defaults",
key: "com.android.os.statsd.key",
@@ -72,4 +73,4 @@
"com.android.os.statsd",
"test_com.android.os.statsd",
],
-}
\ No newline at end of file
+}
diff --git a/api/current.txt b/api/current.txt
index 7c80f5e..76dad98 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4351,7 +4351,7 @@
public class AppOpsManager {
method @Deprecated public int checkOp(@NonNull String, int, @NonNull String);
method @Deprecated public int checkOpNoThrow(@NonNull String, int, @NonNull String);
- method public void checkPackage(int, @NonNull String);
+ method @Deprecated public void checkPackage(int, @NonNull String);
method @Deprecated public void finishOp(@NonNull String, int, @NonNull String);
method public void finishOp(@NonNull String, int, @NonNull String, @Nullable String);
method public boolean isOpActive(@NonNull String, int, @NonNull String);
@@ -4364,7 +4364,7 @@
method @Deprecated public int noteProxyOpNoThrow(@NonNull String, @NonNull String);
method @Deprecated public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int);
method public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int, @Nullable String, @Nullable String);
- method public static String permissionToOp(String);
+ method @Nullable public static String permissionToOp(@NonNull String);
method public void setNotedAppOpsCollector(@Nullable android.app.AppOpsManager.AppOpsCollector);
method @Deprecated public int startOp(@NonNull String, int, @NonNull String);
method public int startOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String);
@@ -10827,6 +10827,8 @@
field public static final int FLAG_ACTIVITY_NO_USER_ACTION = 262144; // 0x40000
field public static final int FLAG_ACTIVITY_PREVIOUS_IS_TOP = 16777216; // 0x1000000
field public static final int FLAG_ACTIVITY_REORDER_TO_FRONT = 131072; // 0x20000
+ field public static final int FLAG_ACTIVITY_REQUIRE_DEFAULT = 512; // 0x200
+ field public static final int FLAG_ACTIVITY_REQUIRE_NON_BROWSER = 1024; // 0x400
field public static final int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 2097152; // 0x200000
field public static final int FLAG_ACTIVITY_RETAIN_IN_RECENTS = 8192; // 0x2000
field public static final int FLAG_ACTIVITY_SINGLE_TOP = 536870912; // 0x20000000
@@ -25092,7 +25094,7 @@
public final class MediaCas implements java.lang.AutoCloseable {
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
- ctor public MediaCas(int, @Nullable String, int) throws android.media.MediaCasException.UnsupportedCasException;
+ ctor public MediaCas(@NonNull android.content.Context, int, @Nullable String, int) throws android.media.MediaCasException.UnsupportedCasException;
method public void close();
method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
method protected void finalize();
diff --git a/api/system-current.txt b/api/system-current.txt
index a8c2453..3d14961 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -371,8 +371,8 @@
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[]);
method public static int opToDefaultMode(@NonNull String);
method @Nullable public static String opToPermission(@NonNull String);
- method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(String, int, String, int);
- method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(String, int, int);
+ method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(@NonNull String, int, @Nullable String, int);
+ method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(@NonNull String, int, int);
field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
field public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
@@ -4826,7 +4826,7 @@
method public int getAudioFilterCount();
method public int getDemuxCount();
method public int getFilterCapabilities();
- method @Nullable @Size(5) public int[] getLinkCapabilities();
+ method @NonNull @Size(5) public int[] getLinkCapabilities();
method public int getPcrFilterCount();
method public int getPesFilterCount();
method public int getPlaybackCount();
@@ -4842,7 +4842,7 @@
method public int addPid(int, int, @Nullable android.media.tv.tuner.filter.Filter);
method public void close();
method public int removePid(int, int, @Nullable android.media.tv.tuner.filter.Filter);
- method public int setKeyToken(@Nullable byte[]);
+ method public int setKeyToken(@NonNull byte[]);
field public static final int PID_TYPE_MMTP = 2; // 0x2
field public static final int PID_TYPE_T = 1; // 0x1
}
@@ -4879,10 +4879,11 @@
}
public class Tuner implements java.lang.AutoCloseable {
- ctor @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public Tuner(@NonNull android.content.Context, @NonNull String, int, @Nullable android.media.tv.tuner.Tuner.OnResourceLostListener);
+ ctor @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public Tuner(@NonNull android.content.Context, @Nullable String, int);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int cancelScanning();
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int cancelTuning();
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearOnTuneEventListener();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearResourceLostListener();
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void close();
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int connectCiCam(int);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int disconnectCiCam();
@@ -4892,15 +4893,16 @@
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
+ method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
+ method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.filter.Filter openFilter(int, int, long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.filter.FilterCallback);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Lnb openLnb(@Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.LnbCallback);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Lnb openLnbByName(@Nullable String, @Nullable java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
+ method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Lnb openLnb(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
+ method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Lnb openLnbByName(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
method @Nullable public android.media.tv.tuner.filter.TimeFilter openTimeFilter();
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setLna(boolean);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setLnaEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void updateResourcePriority(int, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 79d29f6..957794c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -180,8 +180,8 @@
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void resetHistoryParameters();
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void setHistoryParameters(int, long, int);
method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(int, int, String, int);
- method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(String, int, String, int);
- method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(String, int, int);
+ method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(@NonNull String, int, @Nullable String, int);
+ method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(@NonNull String, int, int);
method public static int strOpToOp(@NonNull String);
field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0
field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 41a1706..66f5c39 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -146,6 +146,7 @@
host_supported: true,
srcs: [
"idmap2/Create.cpp",
+ "idmap2/CreateMultiple.cpp",
"idmap2/Dump.cpp",
"idmap2/Lookup.cpp",
"idmap2/Main.cpp",
diff --git a/cmds/idmap2/idmap2/Commands.h b/cmds/idmap2/idmap2/Commands.h
index 718e361..e626738 100644
--- a/cmds/idmap2/idmap2/Commands.h
+++ b/cmds/idmap2/idmap2/Commands.h
@@ -23,6 +23,7 @@
#include "idmap2/Result.h"
android::idmap2::Result<android::idmap2::Unit> Create(const std::vector<std::string>& args);
+android::idmap2::Result<android::idmap2::Unit> CreateMultiple(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Dump(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Lookup(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Scan(const std::vector<std::string>& args);
diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp
new file mode 100644
index 0000000..0b0541f
--- /dev/null
+++ b/cmds/idmap2/idmap2/CreateMultiple.cpp
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ */
+
+#include <sys/stat.h> // umask
+#include <sys/types.h> // umask
+
+#include <fstream>
+#include <memory>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+#include "idmap2/BinaryStreamVisitor.h"
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/FileUtils.h"
+#include "idmap2/Idmap.h"
+#include "idmap2/Policies.h"
+#include "idmap2/SysTrace.h"
+
+using android::ApkAssets;
+using android::base::StringPrintf;
+using android::idmap2::BinaryStreamVisitor;
+using android::idmap2::CommandLineOptions;
+using android::idmap2::Error;
+using android::idmap2::Idmap;
+using android::idmap2::PoliciesToBitmask;
+using android::idmap2::PolicyBitmask;
+using android::idmap2::PolicyFlags;
+using android::idmap2::Result;
+using android::idmap2::Unit;
+using android::idmap2::utils::kIdmapCacheDir;
+using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::UidHasWriteAccessToPath;
+
+Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
+ SYSTRACE << "CreateMultiple " << args;
+ std::string target_apk_path;
+ std::string idmap_dir = kIdmapCacheDir;
+ std::vector<std::string> overlay_apk_paths;
+ std::vector<std::string> policies;
+ bool ignore_overlayable = false;
+
+ const CommandLineOptions opts =
+ CommandLineOptions("idmap2 create-multiple")
+ .MandatoryOption("--target-apk-path",
+ "input: path to apk which will have its resources overlaid",
+ &target_apk_path)
+ .MandatoryOption("--overlay-apk-path",
+ "input: path to apk which contains the new resource values",
+ &overlay_apk_paths)
+ .OptionalOption("--idmap-dir",
+ StringPrintf("output: path to the directory in which to write idmap file"
+ " (defaults to %s)",
+ kIdmapCacheDir),
+ &idmap_dir)
+ .OptionalOption("--policy",
+ "input: an overlayable policy this overlay fulfills"
+ " (if none or supplied, the overlay policy will default to \"public\")",
+ &policies)
+ .OptionalFlag("--ignore-overlayable", "disables overlayable and policy checks",
+ &ignore_overlayable);
+ const auto opts_ok = opts.Parse(args);
+ if (!opts_ok) {
+ return opts_ok.GetError();
+ }
+
+ PolicyBitmask fulfilled_policies = 0;
+ auto conv_result = PoliciesToBitmask(policies);
+ if (conv_result) {
+ fulfilled_policies |= *conv_result;
+ } else {
+ return conv_result.GetError();
+ }
+
+ if (fulfilled_policies == 0) {
+ fulfilled_policies |= PolicyFlags::POLICY_PUBLIC;
+ }
+
+ const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ if (!target_apk) {
+ return Error("failed to load apk %s", target_apk_path.c_str());
+ }
+
+ std::vector<std::string> idmap_paths;
+ for (const std::string& overlay_apk_path : overlay_apk_paths) {
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(idmap_dir, overlay_apk_path);
+ const uid_t uid = getuid();
+ if (!UidHasWriteAccessToPath(uid, idmap_path)) {
+ LOG(WARNING) << "uid " << uid << "does not have write access to " << idmap_path.c_str();
+ continue;
+ }
+
+ const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ if (!overlay_apk) {
+ LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str();
+ continue;
+ }
+
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
+ if (!idmap) {
+ LOG(WARNING) << "failed to create idmap";
+ continue;
+ }
+
+ umask(kIdmapFilePermissionMask);
+ std::ofstream fout(idmap_path);
+ if (fout.fail()) {
+ LOG(WARNING) << "failed to open idmap path " << idmap_path.c_str();
+ continue;
+ }
+
+ BinaryStreamVisitor visitor(fout);
+ (*idmap)->accept(&visitor);
+ fout.close();
+ if (fout.fail()) {
+ LOG(WARNING) << "failed to write to idmap path %s" << idmap_path.c_str();
+ continue;
+ }
+
+ idmap_paths.emplace_back(idmap_path);
+ }
+
+ for (const std::string& idmap_path : idmap_paths) {
+ std::cout << idmap_path << std::endl;
+ }
+
+ return Unit{};
+}
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
index 8794908..a07e793 100644
--- a/cmds/idmap2/idmap2/Main.cpp
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -53,7 +53,9 @@
int main(int argc, char** argv) {
SYSTRACE << "main";
const NameToFunctionMap commands = {
- {"create", Create}, {"dump", Dump}, {"lookup", Lookup}, {"scan", Scan}, {"verify", Verify},
+ {"create", Create}, {"create-multiple", CreateMultiple},
+ {"dump", Dump}, {"lookup", Lookup},
+ {"scan", Scan}, {"verify", Verify},
};
if (argc <= 1) {
PrintUsage(commands, std::cerr);
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 6394ab2..93522d4 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -169,6 +169,11 @@
"libcutils",
"libstatslog",
],
+ apex_available: [
+ //TODO(b/149782403): Remove this once statsd no longer links against libstatsmetadata
+ "com.android.os.statsd",
+ "test_com.android.os.statsd",
+ ],
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 2838ad8..0293346 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -694,7 +694,7 @@
/** @hide Should this process state be considered a background state? */
public static final boolean isProcStateBackground(int procState) {
- return procState >= PROCESS_STATE_TRANSIENT_BACKGROUND;
+ return procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
}
/** @hide Is this a foreground service type? */
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index f6bbc68..9ed4798 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -97,15 +97,77 @@
import java.util.function.Supplier;
/**
- * AppOps are mappings of [package/uid, op-name] -> [mode]. The list of existing appops is defined
- * by the system and cannot be amended by apps. Only system apps can change appop-modes.
+ * App-ops are used for two purposes: Access control and tracking.
*
- * <p>Beside a mode the system tracks when an op was {@link #noteOp noted}. The tracked data can
- * only be read by system components.
+ * <p>App-ops cover a wide variety of functionality from helping with runtime permissions access
+ * control and tracking to battery consumption tracking.
*
- * <p>Installed apps can usually only listen to changes and events on their own ops. E.g.
- * {@link AppOpsCollector} allows to get a callback each time an app called {@link #noteOp} or
- * {@link #startOp} for an op belonging to the app.
+ * <h2>Access control</h2>
+ *
+ * <p>App-ops can either be controlled for each uid or for each package. Which one is used depends
+ * on the API provider maintaining this app-op. For any security or privacy related app-op the
+ * provider needs to control the app-op for per uid as all security and privacy is based on uid in
+ * Android.
+ *
+ * <p>To control access the app-op can be set to a mode to:
+ * <dl>
+ * <dt>{@link #MODE_DEFAULT}
+ * <dd>Default behavior, might differ from app-op or app-op
+ * <dt>{@link #MODE_ALLOWED}
+ * <dd>Allow the access
+ * <dt>{@link #MODE_IGNORED}
+ * <dd>Don't allow the access, i.e. don't perform the requested action or return no or dummy
+ * data
+ * <dt>{@link #MODE_ERRORED}
+ * <dd>Throw a {@link SecurityException} on access. This can be suppressed by using a
+ * {@code ...noThrow} method to check the mode
+ * </dl>
+ *
+ * <p>API providers need to check the mode returned by {@link #noteOp} if they are are allowing
+ * access to operations gated by the app-op. {@link #unsafeCheckOp} should be used to check the
+ * mode if no access is granted. E.g. this can be used for displaying app-op state in the UI or
+ * when checking the state before later calling {@link #noteOp} anyway.
+ *
+ * <p>If an operation refers to a time span (e.g. a audio-recording session) the API provider
+ * should use {@link #startOp} and {@link #finishOp} instead of {@link #noteOp}.
+ *
+ * <h3>Runtime permissions and app-ops</h3>
+ *
+ * <p>Each platform defined runtime permission (beside background modifiers) has an associated app
+ * op which is used for tracking but also to allow for silent failures. I.e. if the runtime
+ * permission is denied the caller gets a {@link SecurityException}, but if the permission is
+ * granted and the app-op is {@link #MODE_IGNORED} then the callers gets dummy behavior, e.g.
+ * location callbacks would not happen.
+ *
+ * <h3>App-op permissions</h3>
+ *
+ * <p>App-ops permissions are platform defined permissions that can be overridden. The security
+ * check for app-op permissions should by {@link #MODE_DEFAULT default} check the permission grant
+ * state. If the app-op state is set to {@link #MODE_ALLOWED} or {@link #MODE_IGNORED} the app-op
+ * state should be checked instead of the permission grant state.
+ *
+ * <p>This functionality allows to grant access by default to apps fulfilling the requirements for
+ * a certain permission level. Still the behavior can be overridden when needed.
+ *
+ * <h2>Tracking</h2>
+ *
+ * <p>App-ops track many important events, including all accesses to runtime permission protected
+ * APIs. This is done by tracking when an app-op was {@link #noteOp noted} or
+ * {@link #startOp started}. The tracked data can only be read by system components.
+ *
+ * <p><b>Only {@link #noteOp}/{@link #startOp} are tracked; {@link #unsafeCheckOp} is not tracked.
+ * Hence it is important to eventually call {@link #noteOp} or {@link #startOp} when providing
+ * access to protected operations or data.</b>
+ *
+ * <p>Some apps are forwarding access to other apps. E.g. an app might get the location from the
+ * system's location provider and then send the location further to a 3rd app. In this case the
+ * app passing on the data needs to call {@link #noteProxyOp} to signal the access proxying. This
+ * might also make sense inside of a single app if the access is forwarded between two features of
+ * the app.
+ *
+ * <p>An app can register an {@link AppOpsCollector} to get informed about what accesses the
+ * system is tracking for it. As each runtime permission has an associated app-op this API is
+ * particularly useful for an app that want to find unexpected private data accesses.
*/
@SystemService(Context.APP_OPS_SERVICE)
public class AppOpsManager {
@@ -121,28 +183,6 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
public static final long CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE = 148180766L;
- /**
- * <p>App ops allows callers to:</p>
- *
- * <ul>
- * <li> Note when operations are happening, and find out if they are allowed for the current
- * caller.</li>
- * <li> Disallow specific apps from doing specific operations.</li>
- * <li> Collect all of the current information about operations that have been executed or
- * are not being allowed.</li>
- * <li> Monitor for changes in whether an operation is allowed.</li>
- * </ul>
- *
- * <p>Each operation is identified by a single integer; these integers are a fixed set of
- * operations, enumerated by the OP_* constants.
- *
- * <p></p>When checking operations, the result is a "mode" integer indicating the current
- * setting for the operation under that caller: MODE_ALLOWED, MODE_IGNORED (don't execute
- * the operation but fake its behavior enough so that the caller doesn't crash),
- * MODE_ERRORED (throw a SecurityException back to the caller; the normal operation calls
- * will do this for you).
- */
-
final Context mContext;
@UnsupportedAppUsage
@@ -1093,7 +1133,7 @@
/** Required to draw on top of other apps. */
public static final String OPSTR_SYSTEM_ALERT_WINDOW
= "android:system_alert_window";
- /** Required to write/modify/update system settingss. */
+ /** Required to write/modify/update system settings. */
public static final String OPSTR_WRITE_SETTINGS
= "android:write_settings";
/** @hide Get device accounts. */
@@ -6115,7 +6155,7 @@
*/
public interface OnOpActiveChangedListener {
/**
- * Called when the active state of an app op changes.
+ * Called when the active state of an app-op changes.
*
* @param op The operation that changed.
* @param packageName The package performing the operation.
@@ -6406,7 +6446,7 @@
@SystemApi
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
- public void setUidMode(String appOp, int uid, @Mode int mode) {
+ public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) {
try {
mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
} catch (RemoteException e) {
@@ -6461,7 +6501,8 @@
@TestApi
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
- public void setMode(String op, int uid, String packageName, @Mode int mode) {
+ public void setMode(@NonNull String op, int uid, @Nullable String packageName,
+ @Mode int mode) {
try {
mService.setMode(strOpToOp(op), uid, packageName, mode);
} catch (RemoteException e) {
@@ -6504,16 +6545,17 @@
}
/**
- * Gets the app op name associated with a given permission.
- * The app op name is one of the public constants defined
+ * Gets the app-op name associated with a given permission.
+ *
+ * <p>The app-op name is one of the public constants defined
* in this class such as {@link #OPSTR_COARSE_LOCATION}.
* This API is intended to be used for mapping runtime
- * permissions to the corresponding app op.
+ * permissions to the corresponding app-op.
*
* @param permission The permission.
- * @return The app op associated with the permission or null.
+ * @return The app-op associated with the permission or {@code null}.
*/
- public static String permissionToOp(String permission) {
+ public static @Nullable String permissionToOp(@NonNull String permission) {
final Integer opCode = sPermToOp.get(permission);
if (opCode == null) {
return null;
@@ -6631,7 +6673,7 @@
}
/**
- * Start watching for changes to the active state of app ops. An app op may be
+ * Start watching for changes to the active state of app-ops. An app-op may be
* long running and it has a clear start and stop delimiters. If an op is being
* started or stopped by any package you will get a callback. To change the
* watched ops for a registered callback you need to unregister and register it
@@ -6690,7 +6732,7 @@
}
/**
- * Stop watching for changes to the active state of an app op. An app op may be
+ * Stop watching for changes to the active state of an app-op. An app-op may be
* long running and it has a clear start and stop delimiters. Unregistering a
* non-registered callback has no effect.
*
@@ -6921,7 +6963,8 @@
* @param op The operation to note. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
- * @param featureId The feature in the app or {@code null} for default feature
+ * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code
+ * null} for default feature
* @param message A message describing the reason the op was noted
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -6996,6 +7039,8 @@
* @param op The operation to note. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
+ * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code
+ * null} for default feature
* @param message A message describing the reason the op was noted
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7003,8 +7048,8 @@
* causing the app to crash).
*/
public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
- @Nullable String feature, @Nullable String message) {
- return noteOpNoThrow(strOpToOp(op), uid, packageName, feature, message);
+ @Nullable String featureId, @Nullable String message) {
+ return noteOpNoThrow(strOpToOp(op), uid, packageName, featureId, message);
}
/**
@@ -7273,11 +7318,9 @@
}
/**
- * Do a quick check to validate if a package name belongs to a UID.
- *
- * @throws SecurityException if the package name doesn't belong to the given
- * UID, or if ownership cannot be verified.
+ * @deprecated Use {@link PackageManager#getPackageUid} instead
*/
+ @Deprecated
public void checkPackage(int uid, @NonNull String packageName) {
try {
if (mService.checkPackage(uid, packageName) != MODE_ALLOWED) {
@@ -7396,7 +7439,8 @@
* @param op The operation to start. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
- * @param featureId The feature in the app or {@code null} for default feature
+ * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code
+ * null} for default feature
* @param message Description why op was started
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7587,7 +7631,8 @@
}
/**
- * Checks whether the given op for a package is active.
+ * Checks whether the given op for a package is active, i.e. did someone call {@link #startOp}
+ * without {@link #finishOp} yet.
* <p>
* If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS}
* permission you can query only for your UID.
@@ -7917,18 +7962,19 @@
}
/**
- * Callback an app can choose to {@link #setNotedAppOpsCollector register} to monitor it's noted
- * appops. I.e. each time any app calls {@link #noteOp} or {@link #startOp} one of the callback
- * methods of this object is called.
+ * Callback an app can {@link #setNotedAppOpsCollector register} to monitor the app-ops the
+ * system has tracked for it. I.e. each time any app calls {@link #noteOp} or {@link #startOp}
+ * one of the callback methods of this object is called.
*
- * <p><b>Only appops related to dangerous permissions are collected.</b>
+ * <p><b>There will be a callback for all app-ops related to runtime permissions, but not
+ * necessarily for all other app-ops.
*
* <pre>
* setNotedAppOpsCollector(new AppOpsCollector() {
* ArraySet<Pair<String, String>> opsNotedForThisProcess = new ArraySet<>();
*
* private synchronized void addAccess(String op, String accessLocation) {
- * // Ops are often noted when permission protected APIs were called.
+ * // Ops are often noted when runtime permission protected APIs were called.
* // In this case permissionToOp() allows to resolve the permission<->op
* opsNotedForThisProcess.add(new Pair(accessType, accessLocation));
* }
@@ -7970,20 +8016,21 @@
}
/**
- * Called when an app-op was noted for this package inside of a two-way binder-call.
+ * Called when an app-op was {@link #noteOp noted} for this package inside of a synchronous
+ * API call, i.e. a API call that returned data or waited until the action was performed.
*
- * <p>Called on the calling thread just after executing the binder-call. This allows
- * the app to e.g. collect stack traces to figure out where the access came from.
+ * <p>Called on the calling thread before the API returns. This allows the app to e.g.
+ * collect stack traces to figure out where the access came from.
*
* @param op The op noted
*/
public abstract void onNoted(@NonNull SyncNotedAppOp op);
/**
- * Called when this app noted an app-op for its own package.
+ * Called when this app noted an app-op for its own package,
*
- * <p>Called on the thread the noted the op. This allows the app to e.g. collect stack
- * traces to figure out where the access came from.
+ * <p>This is very similar to {@link #onNoted} only that the tracking was not caused by the
+ * API provider in a separate process, but by one in the app's own process.
*
* @param op The op noted
*/
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 576b56f..32e7d84 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6046,21 +6046,18 @@
/**
* Removes RemoteViews that were created for compatibility from {@param n}, if they did not
- * change. Also removes extenders on low ram devices, as
- * {@link android.service.notification.NotificationListenerService} services are disabled.
+ * change.
*
* @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
*
* @hide
*/
- public static Notification maybeCloneStrippedForDelivery(Notification n, boolean isLowRam,
- Context context) {
+ public static Notification maybeCloneStrippedForDelivery(Notification n) {
String templateClass = n.extras.getString(EXTRA_TEMPLATE);
// Only strip views for known Styles because we won't know how to
// re-create them otherwise.
- if (!isLowRam
- && !TextUtils.isEmpty(templateClass)
+ if (!TextUtils.isEmpty(templateClass)
&& getNotificationStyleClass(templateClass) == null) {
return n;
}
@@ -6077,8 +6074,7 @@
n.headsUpContentView.getSequenceNumber();
// Nothing to do here, no need to clone.
- if (!isLowRam
- && !stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
+ if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
return n;
}
@@ -6095,15 +6091,6 @@
clone.headsUpContentView = null;
clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
}
- if (isLowRam) {
- String[] allowedServices = context.getResources().getStringArray(
- R.array.config_allowedManagedServicesOnLowRamDevices);
- if (allowedServices.length == 0) {
- clone.extras.remove(Notification.TvExtender.EXTRA_TV_EXTENDER);
- clone.extras.remove(WearableExtender.EXTRA_WEARABLE_EXTENSIONS);
- clone.extras.remove(CarExtender.EXTRA_CAR_EXTENDER);
- }
- }
return clone;
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 528b508..88edb05 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -592,10 +592,7 @@
}
notification.reduceImageSizes(mContext);
-
- ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
- boolean isLowRam = am.isLowRamDevice();
- return Builder.maybeCloneStrippedForDelivery(notification, isLowRam, mContext);
+ return Builder.maybeCloneStrippedForDelivery(notification);
}
private void fixLegacySmallIcon(Notification n, String pkg) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2e591ca..acc4cb0 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6478,6 +6478,21 @@
public static final int FLAG_ACTIVITY_MATCH_EXTERNAL = 0x00000800;
/**
+ * If set in an intent passed to {@link Context#startActivity Context.startActivity()}, this
+ * flag will only launch the intent if it resolves to a result that is not a browser. If no such
+ * result exists, an {@link ActivityNotFoundException} will be thrown.
+ */
+ public static final int FLAG_ACTIVITY_REQUIRE_NON_BROWSER = 0x00000400;
+
+ /**
+ * If set in an intent passed to {@link Context#startActivity Context.startActivity()}, this
+ * flag will only launch the intent if it resolves to a single result. If no such result exists
+ * or if the system chooser would otherwise be displayed, an {@link ActivityNotFoundException}
+ * will be thrown.
+ */
+ public static final int FLAG_ACTIVITY_REQUIRE_DEFAULT = 0x00000200;
+
+ /**
* If set, when sending a broadcast only registered receivers will be
* called -- no BroadcastReceiver components will be launched.
*/
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index a3487be..55a6cab 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -45,7 +45,7 @@
STATE_NO_IDMAP,
STATE_DISABLED,
STATE_ENABLED,
- STATE_ENABLED_STATIC,
+ STATE_ENABLED_IMMUTABLE,
// @Deprecated STATE_TARGET_IS_BEING_REPLACED,
STATE_OVERLAY_IS_BEING_REPLACED,
})
@@ -117,11 +117,12 @@
/**
* The overlay package is currently enabled because it is marked as
- * 'static'. It cannot be disabled but will change state if for instance
+ * 'immutable'. It cannot be disabled but will change state if for instance
* its target is uninstalled.
* @hide
*/
- public static final int STATE_ENABLED_STATIC = 6;
+ @Deprecated
+ public static final int STATE_ENABLED_IMMUTABLE = 6;
/**
* Overlay category: theme.
@@ -180,21 +181,21 @@
public final int userId;
/**
- * Priority as read from the manifest. Used if isStatic is true. Not
- * intended to be exposed to 3rd party.
+ * Priority as configured by {@link com.android.internal.content.om.OverlayConfig}.
+ * Not intended to be exposed to 3rd party.
*
* @hide
*/
public final int priority;
/**
- * isStatic as read from the manifest. If true, the overlay is
- * unconditionally loaded and cannot be unloaded. Not intended to be
+ * isMutable as configured by {@link com.android.internal.content.om.OverlayConfig}.
+ * If false, the overlay is unconditionally loaded and cannot be unloaded. Not intended to be
* exposed to 3rd party.
*
* @hide
*/
- public final boolean isStatic;
+ public final boolean isMutable;
/**
* Create a new OverlayInfo based on source with an updated state.
@@ -207,14 +208,14 @@
public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
this(source.packageName, source.targetPackageName, source.targetOverlayableName,
source.category, source.baseCodePath, state, source.userId, source.priority,
- source.isStatic);
+ source.isMutable);
}
/** @hide */
public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
@Nullable String targetOverlayableName, @Nullable String category,
@NonNull String baseCodePath, int state, int userId,
- int priority, boolean isStatic) {
+ int priority, boolean isMutable) {
this.packageName = packageName;
this.targetPackageName = targetPackageName;
this.targetOverlayableName = targetOverlayableName;
@@ -223,7 +224,7 @@
this.state = state;
this.userId = userId;
this.priority = priority;
- this.isStatic = isStatic;
+ this.isMutable = isMutable;
ensureValidState();
}
@@ -237,7 +238,7 @@
state = source.readInt();
userId = source.readInt();
priority = source.readInt();
- isStatic = source.readBoolean();
+ isMutable = source.readBoolean();
ensureValidState();
}
@@ -307,7 +308,7 @@
case STATE_NO_IDMAP:
case STATE_DISABLED:
case STATE_ENABLED:
- case STATE_ENABLED_STATIC:
+ case STATE_ENABLED_IMMUTABLE:
case STATE_TARGET_IS_BEING_REPLACED:
case STATE_OVERLAY_IS_BEING_REPLACED:
break;
@@ -331,7 +332,7 @@
dest.writeInt(state);
dest.writeInt(userId);
dest.writeInt(priority);
- dest.writeBoolean(isStatic);
+ dest.writeBoolean(isMutable);
}
public static final @android.annotation.NonNull Parcelable.Creator<OverlayInfo> CREATOR =
@@ -360,7 +361,7 @@
public boolean isEnabled() {
switch (state) {
case STATE_ENABLED:
- case STATE_ENABLED_STATIC:
+ case STATE_ENABLED_IMMUTABLE:
return true;
default:
return false;
@@ -386,8 +387,8 @@
return "STATE_DISABLED";
case STATE_ENABLED:
return "STATE_ENABLED";
- case STATE_ENABLED_STATIC:
- return "STATE_ENABLED_STATIC";
+ case STATE_ENABLED_IMMUTABLE:
+ return "STATE_ENABLED_IMMUTABLE";
case STATE_TARGET_IS_BEING_REPLACED:
return "STATE_TARGET_IS_BEING_REPLACED";
case STATE_OVERLAY_IS_BEING_REPLACED:
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 03ed373..5a56b0e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3023,6 +3023,18 @@
public static final String FEATURE_TUNER = "android.hardware.tv.tuner";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * the necessary changes to support app enumeration.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_APP_ENUMERATION = "android.software.app_enumeration";
+
+ /** @hide */
+ public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
+
+ /**
* Extra field name for the URI to a verification file. Passed to a package
* verifier.
*
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index da44f70..168679e 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -483,6 +483,9 @@
public final boolean isolatedSplits;
public final boolean isSplitRequired;
public final boolean useEmbeddedDex;
+ public final String targetPackageName;
+ public final boolean overlayIsStatic;
+ public final int overlayPriority;
public ApkLite(String codePath, String packageName, String splitName,
boolean isFeatureSplit,
@@ -492,6 +495,7 @@
SigningDetails signingDetails, boolean coreApp,
boolean debuggable, boolean multiArch, boolean use32bitAbi,
boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits,
+ String targetPackageName, boolean overlayIsStatic, int overlayPriority,
int minSdkVersion, int targetSdkVersion) {
this.codePath = codePath;
this.packageName = packageName;
@@ -513,6 +517,9 @@
this.extractNativeLibs = extractNativeLibs;
this.isolatedSplits = isolatedSplits;
this.isSplitRequired = isSplitRequired;
+ this.targetPackageName = targetPackageName;
+ this.overlayIsStatic = overlayIsStatic;
+ this.overlayPriority = overlayPriority;
this.minSdkVersion = minSdkVersion;
this.targetSdkVersion = targetSdkVersion;
}
@@ -1784,6 +1791,12 @@
boolean useEmbeddedDex = false;
String configForSplit = null;
String usesSplitName = null;
+ String targetPackage = null;
+ boolean overlayIsStatic = false;
+ int overlayPriority = 0;
+
+ String requiredSystemPropertyName = null;
+ String requiredSystemPropertyValue = null;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
final String attr = attrs.getAttributeName(i);
@@ -1848,6 +1861,21 @@
useEmbeddedDex = attrs.getAttributeBooleanValue(i, false);
}
}
+ } else if (PackageParser.TAG_OVERLAY.equals(parser.getName())) {
+ for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+ final String attr = attrs.getAttributeName(i);
+ if ("requiredSystemPropertyName".equals(attr)) {
+ requiredSystemPropertyName = attrs.getAttributeValue(i);
+ } else if ("requiredSystemPropertyValue".equals(attr)) {
+ requiredSystemPropertyValue = attrs.getAttributeValue(i);
+ } else if ("targetPackage".equals(attr)) {
+ targetPackage = attrs.getAttributeValue(i);;
+ } else if ("isStatic".equals(attr)) {
+ overlayIsStatic = attrs.getAttributeBooleanValue(i, false);
+ } else if ("priority".equals(attr)) {
+ overlayPriority = attrs.getAttributeIntValue(i, 0);
+ }
+ }
} else if (TAG_USES_SPLIT.equals(parser.getName())) {
if (usesSplitName != null) {
Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others.");
@@ -1874,11 +1902,22 @@
}
}
+ // Check to see if overlay should be excluded based on system property condition
+ if (!checkRequiredSystemProperty(requiredSystemPropertyName,
+ requiredSystemPropertyValue)) {
+ Slog.i(TAG, "Skipping target and overlay pair " + targetPackage + " and "
+ + codePath + ": overlay ignored due to required system property: "
+ + requiredSystemPropertyName + " with value: " + requiredSystemPropertyValue);
+ targetPackage = null;
+ overlayIsStatic = false;
+ overlayPriority = 0;
+ }
+
return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits,
- minSdkVersion, targetSdkVersion);
+ targetPackage, overlayIsStatic, overlayPriority, minSdkVersion, targetSdkVersion);
}
/**
@@ -2162,7 +2201,7 @@
}
// check to see if overlay should be excluded based on system property condition
- if (!checkOverlayRequiredSystemProperty(propName, propValue)) {
+ if (!checkRequiredSystemProperty(propName, propValue)) {
Slog.i(TAG, "Skipping target and overlay pair " + pkg.mOverlayTarget + " and "
+ pkg.baseCodePath+ ": overlay ignored due to required system property: "
+ propName + " with value: " + propValue);
@@ -2590,8 +2629,11 @@
return pkg;
}
- private boolean checkOverlayRequiredSystemProperty(String propName, String propValue) {
-
+ /**
+ * Returns {@code true} if both the property name and value are empty or if the given system
+ * property is set to the specified value. In all other cases, returns {@code false}
+ */
+ public static boolean checkRequiredSystemProperty(String propName, String propValue) {
if (TextUtils.isEmpty(propName) || TextUtils.isEmpty(propValue)) {
if (!TextUtils.isEmpty(propName) || !TextUtils.isEmpty(propValue)) {
// malformed condition - incomplete
diff --git a/core/java/android/content/pm/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java
new file mode 100644
index 0000000..9b8396e
--- /dev/null
+++ b/core/java/android/content/pm/PackagePartitions.java
@@ -0,0 +1,213 @@
+/*
+ * 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 android.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Environment;
+import android.os.FileUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.function.Function;
+
+/**
+ * Exposes {@link #SYSTEM_PARTITIONS} which represents the partitions in which application packages
+ * can be installed. The partitions are ordered from most generic (lowest priority) to most specific
+ * (greatest priority).
+ *
+ * @hide
+ **/
+public class PackagePartitions {
+ public static final int PARTITION_SYSTEM = 0;
+ public static final int PARTITION_VENDOR = 1;
+ public static final int PARTITION_ODM = 2;
+ public static final int PARTITION_OEM = 3;
+ public static final int PARTITION_PRODUCT = 4;
+ public static final int PARTITION_SYSTEM_EXT = 5;
+
+ @IntDef(flag = true, prefix = { "PARTITION_" }, value = {
+ PARTITION_SYSTEM,
+ PARTITION_VENDOR,
+ PARTITION_ODM,
+ PARTITION_OEM,
+ PARTITION_PRODUCT,
+ PARTITION_SYSTEM_EXT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PartitionType {}
+
+ /**
+ * The list of all system partitions that may contain packages in ascending order of
+ * specificity (the more generic, the earlier in the list a partition appears).
+ */
+ private static final ArrayList<SystemPartition> SYSTEM_PARTITIONS =
+ new ArrayList<>(Arrays.asList(
+ new SystemPartition(Environment.getRootDirectory(), PARTITION_SYSTEM,
+ true /* containsPrivApp */, false /* containsOverlay */),
+ new SystemPartition(Environment.getVendorDirectory(), PARTITION_VENDOR,
+ true /* containsPrivApp */, true /* containsOverlay */),
+ new SystemPartition(Environment.getOdmDirectory(), PARTITION_ODM,
+ true /* containsPrivApp */, true /* containsOverlay */),
+ new SystemPartition(Environment.getOemDirectory(), PARTITION_OEM,
+ false /* containsPrivApp */, true /* containsOverlay */),
+ new SystemPartition(Environment.getProductDirectory(), PARTITION_PRODUCT,
+ true /* containsPrivApp */, true /* containsOverlay */),
+ new SystemPartition(Environment.getSystemExtDirectory(), PARTITION_SYSTEM_EXT,
+ true /* containsPrivApp */, true /* containsOverlay */)));
+
+ /**
+ * Returns a list in which the elements are products of the specified function applied to the
+ * list of {@link #SYSTEM_PARTITIONS} in increasing specificity order.
+ */
+ public static <T> ArrayList<T> getOrderedPartitions(
+ @NonNull Function<SystemPartition, T> producer) {
+ final ArrayList<T> out = new ArrayList<>();
+ for (int i = 0, n = SYSTEM_PARTITIONS.size(); i < n; i++) {
+ final T v = producer.apply(SYSTEM_PARTITIONS.get(i));
+ if (v != null) {
+ out.add(v);
+ }
+ }
+ return out;
+ }
+
+ /** Represents a partition that contains application packages. */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public static class SystemPartition {
+ @NonNull
+ public final File folder;
+
+ @PartitionType
+ public final int type;
+
+ @Nullable
+ private final DeferredCanonicalFile mAppFolder;
+
+ @Nullable
+ private final DeferredCanonicalFile mPrivAppFolder;
+
+ @Nullable
+ private final DeferredCanonicalFile mOverlayFolder;
+
+ private SystemPartition(@NonNull File folder, @PartitionType int type,
+ boolean containsPrivApp, boolean containsOverlay) {
+ this.folder = folder;
+ this.type = type;
+ this.mAppFolder = new DeferredCanonicalFile(folder, "app");
+ this.mPrivAppFolder = containsPrivApp ?
+ new DeferredCanonicalFile(folder, "priv-app") : null;
+ this.mOverlayFolder = containsOverlay ?
+ new DeferredCanonicalFile(folder, "overlay") : null;
+ }
+
+ public SystemPartition(@NonNull SystemPartition original) {
+ this.folder = original.folder;
+ this.type = original.type;
+ this.mAppFolder = original.mAppFolder;
+ this.mPrivAppFolder = original.mPrivAppFolder;
+ this.mOverlayFolder = original.mOverlayFolder;
+ }
+
+ /**
+ * Creates a partition containing the same folders as the original partition but with a
+ * different root folder.
+ */
+ public SystemPartition(@NonNull File rootFolder, @NonNull SystemPartition partition) {
+ this(rootFolder, partition.type, partition.mPrivAppFolder != null,
+ partition.mOverlayFolder != null);
+ }
+
+ /** Returns the canonical app folder of the partition. */
+ @Nullable
+ public File getAppFolder() {
+ return mAppFolder == null ? null : mAppFolder.getFile();
+ }
+
+ /** Returns the canonical priv-app folder of the partition, if one exists. */
+ @Nullable
+ public File getPrivAppFolder() {
+ return mPrivAppFolder == null ? null : mPrivAppFolder.getFile();
+ }
+
+ /** Returns the canonical overlay folder of the partition, if one exists. */
+ @Nullable
+ public File getOverlayFolder() {
+ return mOverlayFolder == null ? null : mOverlayFolder.getFile();
+ }
+
+ /** Returns whether the partition contains the specified file in its priv-app folder. */
+ public boolean containsPrivApp(@NonNull File scanFile) {
+ return FileUtils.contains(mPrivAppFolder.getFile(), scanFile);
+ }
+
+ /** Returns whether the partition contains the specified file in its app folder. */
+ public boolean containsApp(@NonNull File scanFile) {
+ return FileUtils.contains(mAppFolder.getFile(), scanFile);
+ }
+
+ /** Returns whether the partition contains the specified file in its overlay folder. */
+ public boolean containsOverlay(@NonNull File scanFile) {
+ return FileUtils.contains(mOverlayFolder.getFile(), scanFile);
+ }
+
+ /** Returns whether the partition contains the specified file. */
+ public boolean containsPath(@NonNull String path) {
+ return path.startsWith(folder.getPath() + "/");
+ }
+
+ /** Returns whether the partition contains the specified file in its priv-app folder. */
+ public boolean containsPrivPath(@NonNull String path) {
+ return mPrivAppFolder != null
+ && path.startsWith(mPrivAppFolder.getFile().getPath() + "/");
+ }
+ }
+
+ /**
+ * A class that defers the canonicalization of its underlying file. This must be done so
+ * processes do not attempt to canonicalize files in directories for which the process does not
+ * have the correct selinux policies.
+ */
+ private static class DeferredCanonicalFile {
+ private boolean mIsCanonical;
+ private File mFile;
+ private DeferredCanonicalFile(File dir, String fileName) {
+ mFile = new File(dir, fileName);
+ mIsCanonical = false;
+ }
+
+ private File getFile() {
+ if (mIsCanonical) {
+ return mFile;
+ }
+ mIsCanonical = true;
+ try {
+ mFile = mFile.getCanonicalFile();
+ } catch (IOException ignore) {
+ // failed to look up canonical path, continue with original one
+ }
+ return mFile;
+ }
+ }
+}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 5be9c91..9087f42 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -289,6 +289,12 @@
boolean useEmbeddedDex = false;
String configForSplit = null;
String usesSplitName = null;
+ String targetPackage = null;
+ boolean overlayIsStatic = false;
+ int overlayPriority = 0;
+
+ String requiredSystemPropertyName = null;
+ String requiredSystemPropertyValue = null;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
final String attr = attrs.getAttributeName(i);
@@ -365,6 +371,21 @@
break;
}
}
+ } else if (PackageParser.TAG_OVERLAY.equals(parser.getName())) {
+ for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+ final String attr = attrs.getAttributeName(i);
+ if ("requiredSystemPropertyName".equals(attr)) {
+ requiredSystemPropertyName = attrs.getAttributeValue(i);
+ } else if ("requiredSystemPropertyValue".equals(attr)) {
+ requiredSystemPropertyValue = attrs.getAttributeValue(i);
+ } else if ("targetPackage".equals(attr)) {
+ targetPackage = attrs.getAttributeValue(i);;
+ } else if ("isStatic".equals(attr)) {
+ overlayIsStatic = attrs.getAttributeBooleanValue(i, false);
+ } else if ("priority".equals(attr)) {
+ overlayPriority = attrs.getAttributeIntValue(i, 0);
+ }
+ }
} else if (PackageParser.TAG_USES_SPLIT.equals(parser.getName())) {
if (usesSplitName != null) {
Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others.");
@@ -391,11 +412,23 @@
}
}
+ // Check to see if overlay should be excluded based on system property condition
+ if (!PackageParser.checkRequiredSystemProperty(requiredSystemPropertyName,
+ requiredSystemPropertyValue)) {
+ Slog.i(TAG, "Skipping target and overlay pair " + targetPackage + " and "
+ + codePath + ": overlay ignored due to required system property: "
+ + requiredSystemPropertyName + " with value: " + requiredSystemPropertyValue);
+ targetPackage = null;
+ overlayIsStatic = false;
+ overlayPriority = 0;
+ }
+
return new PackageParser.ApkLite(codePath, packageSplit.first, packageSplit.second,
isFeatureSplit, configForSplit, usesSplitName, isSplitRequired, versionCode,
versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails,
coreApp, debuggable, multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs,
- isolatedSplits, minSdkVersion, targetSdkVersion);
+ isolatedSplits, targetPackage, overlayIsStatic, overlayPriority, minSdkVersion,
+ targetSdkVersion);
}
public static VerifierInfo parseVerifier(AttributeSet attrs) {
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 1b01758..f295f8c 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -38,6 +38,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.om.OverlayConfig;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
@@ -242,14 +243,11 @@
try {
final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
apkAssets.add(ApkAssets.loadFromPath(frameworkPath, true /*system*/));
- final String[] systemIdmapPaths = nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
- if (systemIdmapPaths != null) {
- for (String idmapPath : systemIdmapPaths) {
- apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
- }
- } else {
- Log.w(TAG, "'idmap2 --scan' failed: no static=\"true\" overlays targeting "
- + "\"android\" will be loaded");
+
+ final String[] systemIdmapPaths =
+ OverlayConfig.getZygoteInstance().createImmutableFrameworkIdmapsInZygote();
+ for (String idmapPath : systemIdmapPaths) {
+ apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
}
sSystemApkAssetsSet = new ArraySet<>(apkAssets);
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index f19ba0f..2041cfb 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -126,7 +126,11 @@
return getIntentForConfirmation();
}
- /** Delete the VPN profile configuration that was provisioned by the calling app */
+ /**
+ * Delete the VPN profile configuration that was provisioned by the calling app
+ *
+ * @throws SecurityException if this would violate user settings
+ */
public void deleteProvisionedVpnProfile() {
try {
mService.deleteVpnProfile(mContext.getOpPackageName());
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 7d3b13b..f37e01d 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -58,7 +58,7 @@
* </service></pre>
*
* <p> Condition providers cannot be bound by the system on
- * {@link ActivityManager#isLowRamDevice() low ram} devices</p>
+ * {@link ActivityManager#isLowRamDevice() low ram} devices running Android Q (and below)</p>
*
* @deprecated Instead of using an automatically bound service, use
* {@link android.app.NotificationManager#setAutomaticZenRuleState(String, Condition)} to tell the
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 0ff2e03..6562572 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -86,8 +86,8 @@
* or after {@link #onListenerDisconnected()}.
* </p>
* <p> Notification listeners cannot get notification access or be bound by the system on
- * {@linkplain ActivityManager#isLowRamDevice() low-RAM} devices. The system also ignores
- * notification listeners running in a work profile. A
+ * {@linkplain ActivityManager#isLowRamDevice() low-RAM} devices running Android Q (and below).
+ * The system also ignores notification listeners running in a work profile. A
* {@link android.app.admin.DevicePolicyManager} might block notifications originating from a work
* profile.</p>
* <p>
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 44ab596..043e5be 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -666,7 +666,22 @@
int localChanges;
}
+ // If set, ViewRootImpl will call BLASTBufferQueue::setNextTransaction with
+ // mRtBLASTSyncTransaction, prior to invoking draw. This provides a way
+ // to redirect the buffers in to transactions.
private boolean mNextDrawUseBLASTSyncTransaction;
+ // Set when calling setNextTransaction, we can't just reuse mNextDrawUseBLASTSyncTransaction
+ // because, imagine this scenario:
+ // 1. First draw is using BLAST, mNextDrawUseBLAST = true
+ // 2. We call perform draw and are waiting on the callback
+ // 3. After the first perform draw but before the first callback and the
+ // second perform draw, a second draw sets mNextDrawUseBLAST = true (it already was)
+ // 4. At this point the callback fires and we set mNextDrawUseBLAST = false;
+ // 5. We get to performDraw and fail to sync as we intended because mNextDrawUseBLAST
+ // is now false.
+ // This is why we use a two-step latch with the two booleans, one consumed from
+ // performDraw and one consumed from finishBLASTSync()
+ private boolean mNextReportConsumeBLAST;
// Be very careful with the threading here. This is used from the render thread while
// the UI thread is paused and then applied and cleared from the UI thread right after
// draw returns.
@@ -2206,8 +2221,9 @@
return insets;
}
- void dispatchApplyInsets(View host) {
+ public void dispatchApplyInsets(View host) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchApplyInsets");
+ mApplyInsetsRequested = false;
WindowInsets insets = getWindowInsets(true /* forceConstruct */);
final boolean dispatchCutout = (mWindowAttributes.layoutInDisplayCutoutMode
== LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS);
@@ -2444,7 +2460,6 @@
}
if (mApplyInsetsRequested) {
- mApplyInsetsRequested = false;
updateVisibleInsets();
dispatchApplyInsets(host);
if (mLayoutRequested) {
@@ -2621,7 +2636,6 @@
if (contentInsetsChanged || mLastSystemUiVisibility !=
mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested) {
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
- mApplyInsetsRequested = false;
dispatchApplyInsets(host);
// We applied insets so force contentInsetsChanged to ensure the
// hierarchy is measured below.
@@ -3719,9 +3733,9 @@
usingAsyncReport = mReportNextDraw;
if (needFrameCompleteCallback) {
final Handler handler = mAttachInfo.mHandler;
- mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) ->
+ mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
+ finishBLASTSync();
handler.postAtFrontOfQueue(() -> {
- finishBLASTSync();
if (reportNextDraw) {
// TODO: Use the frame number
pendingDrawFinished();
@@ -3731,12 +3745,23 @@
commitCallbacks.get(i).run();
}
}
- }));
+ });});
}
}
try {
if (mNextDrawUseBLASTSyncTransaction) {
+ // TODO(b/149747443)
+ // We aren't prepared to handle overlapping use of mRtBLASTSyncTransaction
+ // so if we are BLAST syncing we make sure the previous draw has
+ // totally finished.
+ if (mAttachInfo.mThreadedRenderer != null) {
+ mAttachInfo.mThreadedRenderer.fence();
+ }
+
+ mNextReportConsumeBLAST = true;
+ mNextDrawUseBLASTSyncTransaction = false;
+
mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
}
boolean canUseAsync = draw(fullRedrawNeeded);
@@ -9556,8 +9581,8 @@
}
private void finishBLASTSync() {
- if (mNextDrawUseBLASTSyncTransaction) {
- mNextDrawUseBLASTSyncTransaction = false;
+ if (mNextReportConsumeBLAST) {
+ mNextReportConsumeBLAST = false;
mRtBLASTSyncTransaction.apply();
}
}
@@ -9566,7 +9591,10 @@
return mRtBLASTSyncTransaction;
}
- SurfaceControl getRenderSurfaceControl() {
+ /**
+ * @hide
+ */
+ public SurfaceControl getRenderSurfaceControl() {
if (mUseBLASTAdapter) {
return mBlastSurfaceControl;
} else {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 54bdb88..ce7cfa7 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1794,6 +1794,9 @@
client.autofillClientResetableStateAvailable();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ // no-op, just log the error message.
+ Log.w(TAG, "Exception getting result from SyncResultReceiver: " + e);
}
}
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index a6a5ec5..47ea1cb 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -1257,7 +1257,8 @@
return;
}
// Show or move the window at the content draw frame.
- mTransaction.deferTransactionUntilSurface(mSurfaceControl, mSurface, frame);
+ mTransaction.deferTransactionUntil(mSurfaceControl, mSurfaceControl,
+ frame);
if (updateWindowPosition) {
mTransaction.setPosition(mSurfaceControl, pendingX, pendingY);
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 5dc8b0b..8f0ffd9 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1304,8 +1304,11 @@
+ "cannot be null.");
}
// We partially rebuild the inactive adapter to determine if we should auto launch
- boolean rebuildActiveCompleted = mMultiProfilePagerAdapter.rebuildActiveTab(true);
- boolean rebuildInactiveCompleted = mMultiProfilePagerAdapter.rebuildInactiveTab(false);
+ boolean rebuildCompleted = mMultiProfilePagerAdapter.rebuildActiveTab(true);
+ if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+ boolean rebuildInactiveCompleted = mMultiProfilePagerAdapter.rebuildInactiveTab(false);
+ rebuildCompleted = rebuildCompleted && rebuildInactiveCompleted;
+ }
if (useLayoutWithDefault()) {
mLayoutId = R.layout.resolver_list_with_default;
@@ -1314,7 +1317,7 @@
}
setContentView(mLayoutId);
mMultiProfilePagerAdapter.setupViewPager(findViewById(R.id.profile_pager));
- return postRebuildList(rebuildActiveCompleted && rebuildInactiveCompleted);
+ return postRebuildList(rebuildCompleted);
}
/**
@@ -1378,6 +1381,10 @@
return false;
}
+ if (mMultiProfilePagerAdapter.getActiveListAdapter().getOtherProfile() != null) {
+ return false;
+ }
+
// Only one target, so we're a candidate to auto-launch!
final TargetInfo target = mMultiProfilePagerAdapter.getActiveListAdapter()
.targetInfoForPosition(0, false);
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
new file mode 100644
index 0000000..f699eb8
--- /dev/null
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -0,0 +1,413 @@
+/*
+ * 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.internal.content.om;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackagePartitions;
+import android.content.pm.parsing.AndroidPackage;
+import android.os.Build;
+import android.os.Process;
+import android.os.Trace;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.om.OverlayConfigParser.OverlayPartition;
+import com.android.internal.content.om.OverlayConfigParser.ParsedConfiguration;
+import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
+import com.android.internal.util.Preconditions;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Responsible for reading overlay configuration files and handling queries of overlay mutability,
+ * default-enabled state, and priority.
+ *
+ * @see OverlayConfigParser
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class OverlayConfig {
+ static final String TAG = "OverlayConfig";
+
+ // The default priority of an overlay that has not been configured. Overlays with default
+ // priority have a higher precedence than configured overlays.
+ @VisibleForTesting
+ public static final int DEFAULT_PRIORITY = Integer.MAX_VALUE;
+
+ @VisibleForTesting
+ public static final class Configuration {
+ @Nullable
+ public final ParsedConfiguration parsedConfig;
+
+ public final int configIndex;
+
+ public Configuration(@Nullable ParsedConfiguration parsedConfig, int configIndex) {
+ this.parsedConfig = parsedConfig;
+ this.configIndex = configIndex;
+ }
+ }
+
+ /**
+ * Interface for providing information on scanned packages.
+ * TODO(147840005): Remove this when android:isStatic and android:priority are fully deprecated
+ */
+ public interface AndroidPackageProvider {
+
+ /** Performs the given action for each package. */
+ void forEachPackage(Consumer<AndroidPackage> p);
+ }
+
+ private static final Comparator<ParsedConfiguration> sStaticOverlayComparator = (c1, c2) -> {
+ final ParsedOverlayInfo o1 = c1.parsedInfo;
+ final ParsedOverlayInfo o2 = c2.parsedInfo;
+ Preconditions.checkArgument(o1.isStatic && o2.isStatic,
+ "attempted to sort non-static overlay");
+
+ if (!o1.targetPackageName.equals(o2.targetPackageName)) {
+ return o1.targetPackageName.compareTo(o2.targetPackageName);
+ }
+
+ final int comparedPriority = o1.priority - o2.priority;
+ return comparedPriority == 0 ? o1.path.compareTo(o2.path) : comparedPriority;
+ };
+
+ // Map of overlay package name to configured overlay settings
+ private final ArrayMap<String, Configuration> mConfigurations = new ArrayMap<>();
+
+ // Singleton instance only assigned in system server
+ private static OverlayConfig sInstance;
+
+ @VisibleForTesting
+ public OverlayConfig(@Nullable File rootDirectory,
+ @Nullable Supplier<OverlayScanner> scannerFactory,
+ @Nullable AndroidPackageProvider packageProvider) {
+ Preconditions.checkArgument((scannerFactory == null) != (packageProvider == null),
+ "scannerFactory and packageProvider cannot be both null or both non-null");
+
+ final ArrayList<OverlayPartition> partitions;
+ if (rootDirectory == null) {
+ partitions = new ArrayList<>(
+ PackagePartitions.getOrderedPartitions(OverlayPartition::new));
+ } else {
+ // Rebase the system partitions and settings file on the specified root directory.
+ partitions = new ArrayList<>(PackagePartitions.getOrderedPartitions(
+ p -> new OverlayPartition(new File(rootDirectory, p.folder.getPath()), p)));
+ }
+
+ boolean foundConfigFile = false;
+ ArrayList<ParsedOverlayInfo> packageManagerOverlayInfos = null;
+
+ final ArrayList<ParsedConfiguration> overlays = new ArrayList<>();
+ for (int i = 0, n = partitions.size(); i < n; i++) {
+ final OverlayPartition partition = partitions.get(i);
+ final OverlayScanner scanner = (scannerFactory == null) ? null : scannerFactory.get();
+ final ArrayList<ParsedConfiguration> partitionOverlays =
+ OverlayConfigParser.getConfigurations(partition, scanner);
+ if (partitionOverlays != null) {
+ foundConfigFile = true;
+ overlays.addAll(partitionOverlays);
+ continue;
+ }
+
+ // If the configuration file is not present, then use android:isStatic and
+ // android:priority to configure the overlays in the partition.
+ // TODO(147840005): Remove converting static overlays to immutable, default-enabled
+ // overlays when android:siStatic and android:priority are fully deprecated.
+ final ArrayList<ParsedOverlayInfo> partitionOverlayInfos;
+ if (scannerFactory != null) {
+ partitionOverlayInfos = new ArrayList<>(scanner.getAllParsedInfos());
+ } else {
+ if (packageManagerOverlayInfos == null) {
+ packageManagerOverlayInfos = getOverlayPackageInfos(packageProvider);
+ }
+
+ // Filter out overlays not present in the partition.
+ partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos);
+ for (int j = partitionOverlayInfos.size() - 1; j >= 0; j--) {
+ if (!partition.containsPath(partitionOverlayInfos.get(j).path.getPath())) {
+ partitionOverlayInfos.remove(j);
+ }
+ }
+ }
+
+ // Static overlays are configured as immutable, default-enabled overlays.
+ final ArrayList<ParsedConfiguration> partitionConfigs = new ArrayList<>();
+ for (int j = 0, m = partitionOverlayInfos.size(); j < m; j++) {
+ final ParsedOverlayInfo p = partitionOverlayInfos.get(j);
+ if (p.isStatic) {
+ partitionConfigs.add(new ParsedConfiguration(p.packageName,
+ true /* enabled */, false /* mutable */, partition.policy, p));
+ }
+ }
+
+ partitionConfigs.sort(sStaticOverlayComparator);
+ overlays.addAll(partitionConfigs);
+ }
+
+ if (!foundConfigFile) {
+ // If no overlay configuration files exist, disregard partition precedence and allow
+ // android:priority to reorder overlays across partition boundaries.
+ overlays.sort(sStaticOverlayComparator);
+ }
+
+ for (int i = 0, n = overlays.size(); i < n; i++) {
+ // Add the configurations to a map so definitions of an overlay in an earlier
+ // partition can be replaced by an overlay with the same package name in a later
+ // partition.
+ final ParsedConfiguration config = overlays.get(i);
+ mConfigurations.put(config.packageName, new Configuration(config, i));
+ }
+ }
+
+ /**
+ * Creates an instance of OverlayConfig for use in the zygote process.
+ * This instance will not include information of static overlays existing outside of a partition
+ * overlay directory.
+ */
+ @NonNull
+ public static OverlayConfig getZygoteInstance() {
+ if (Process.myUid() != Process.ROOT_UID) {
+ // Scan the overlays in the zygote process to generate configuration settings for
+ // overlays on the system image. Do not cache this instance so OverlayConfig will not
+ // be present in applications by default.
+ throw new IllegalStateException("Can only be invoked in the root process");
+ }
+
+ Trace.traceBegin(Trace.TRACE_TAG_RRO, "OverlayConfig#getZygoteInstance");
+ try {
+ return new OverlayConfig(null /* rootDirectory */, OverlayScanner::new,
+ null /* packageProvider */);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RRO);
+ }
+ }
+
+ /**
+ * Initializes a singleton instance for use in the system process.
+ * Can only be called once. This instance is cached so future invocations of
+ * {@link #getSystemInstance()} will return the initialized instance.
+ */
+ @NonNull
+ public static OverlayConfig initializeSystemInstance(AndroidPackageProvider packageProvider) {
+ if (Process.myUid() != Process.SYSTEM_UID) {
+ throw new IllegalStateException("Can only be invoked in the system process");
+ }
+
+ Trace.traceBegin(Trace.TRACE_TAG_RRO, "OverlayConfig#initializeSystemInstance");
+ sInstance = new OverlayConfig(null, null, packageProvider);
+ Trace.traceEnd(Trace.TRACE_TAG_RRO);
+ return sInstance;
+ }
+
+ /**
+ * Retrieves the singleton instance initialized by
+ * {@link #initializeSystemInstance(AndroidPackageProvider)}.
+ */
+ @NonNull
+ public static OverlayConfig getSystemInstance() {
+ if (sInstance == null) {
+ throw new IllegalStateException("System instance not initialized");
+ }
+
+ return sInstance;
+ }
+
+ @VisibleForTesting
+ @Nullable
+ public Configuration getConfiguration(@NonNull String packageName) {
+ return mConfigurations.get(packageName);
+ }
+
+ /**
+ * Returns whether the overlay is enabled by default.
+ * Overlays that are not configured are disabled by default mutable.
+ */
+ public boolean isEnabled(String packageName) {
+ final Configuration config = mConfigurations.get(packageName);
+
+ // STOPSHIP(149499802): Enabling a mutable overlay currently has no effect. Either implement
+ // some behavior for default-enabled, mutable overlays or prevent parsing of the enabled
+ // attribute on overlays that are mutable.
+ if (config != null && config.parsedConfig.mutable) {
+ Log.w(TAG, "Default-enabled configuration for mutable overlay "
+ + config.parsedConfig.packageName + " has no effect");
+ return OverlayConfigParser.DEFAULT_ENABLED_STATE;
+ }
+
+ return config == null? OverlayConfigParser.DEFAULT_ENABLED_STATE
+ : config.parsedConfig.enabled;
+ }
+
+ /**
+ * Returns whether the overlay is mutable and can have its enabled state changed dynamically.
+ * Overlays that are not configured are mutable.
+ */
+ public boolean isMutable(String packageName) {
+ final Configuration config = mConfigurations.get(packageName);
+ return config == null ? OverlayConfigParser.DEFAULT_MUTABILITY
+ : config.parsedConfig.mutable;
+ }
+
+ /**
+ * Returns an integer corresponding to the priority of the overlay.
+ * When multiple overlays override the same resource, the overlay with the highest priority will
+ * will have its value chosen. Overlays that are not configured have a priority of
+ * {@link Integer#MAX_VALUE}.
+ */
+ public int getPriority(String packageName) {
+ final Configuration config = mConfigurations.get(packageName);
+ return config == null ? DEFAULT_PRIORITY : config.configIndex;
+ }
+
+ @NonNull
+ private ArrayList<Configuration> getSortedOverlays() {
+ final ArrayList<Configuration> sortedOverlays = new ArrayList<>();
+ for (int i = 0, n = mConfigurations.size(); i < n; i++) {
+ sortedOverlays.add(mConfigurations.valueAt(i));
+ }
+ sortedOverlays.sort(Comparator.comparingInt(o -> o.configIndex));
+ return sortedOverlays;
+ }
+
+ @NonNull
+ private static ArrayList<ParsedOverlayInfo> getOverlayPackageInfos(
+ @NonNull AndroidPackageProvider packageManager) {
+ final ArrayList<ParsedOverlayInfo> overlays = new ArrayList<>();
+ packageManager.forEachPackage((AndroidPackage p) -> {
+ if (p.getOverlayTarget() != null && p.isSystem()) {
+ overlays.add(new ParsedOverlayInfo(p.getPackageName(), p.getOverlayTarget(),
+ p.getTargetSdkVersion(), p.isOverlayIsStatic(), p.getOverlayPriority(),
+ new File(p.getBaseCodePath())));
+ }
+ });
+ return overlays;
+ }
+
+ /** Represents a single call to idmap create-multiple. */
+ @VisibleForTesting
+ public static class IdmapInvocation {
+ public final boolean enforceOverlayable;
+ public final String policy;
+ public final ArrayList<String> overlayPaths = new ArrayList<>();
+
+ IdmapInvocation(boolean enforceOverlayable, @NonNull String policy) {
+ this.enforceOverlayable = enforceOverlayable;
+ this.policy = policy;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + String.format("{enforceOverlayable=%s, policy=%s"
+ + ", overlayPaths=[%s]}", enforceOverlayable, policy,
+ String.join(", ", overlayPaths));
+ }
+ }
+
+ /**
+ * Retrieves a list of immutable framework overlays in order of least precedence to greatest
+ * precedence.
+ */
+ @VisibleForTesting
+ public ArrayList<IdmapInvocation> getImmutableFrameworkOverlayIdmapInvocations() {
+ final ArrayList<IdmapInvocation> idmapInvocations = new ArrayList<>();
+ final ArrayList<Configuration> sortedConfigs = getSortedOverlays();
+ for (int i = 0, n = sortedConfigs.size(); i < n; i++) {
+ final Configuration overlay = sortedConfigs.get(i);
+ if (overlay.parsedConfig.mutable || !overlay.parsedConfig.enabled
+ || !"android".equals(overlay.parsedConfig.parsedInfo.targetPackageName)) {
+ continue;
+ }
+
+ // Only enforce that overlays targeting packages with overlayable declarations abide by
+ // those declarations if the target sdk of the overlay is at least Q (when overlayable
+ // was introduced).
+ final boolean enforceOverlayable = overlay.parsedConfig.parsedInfo.targetSdkVersion
+ >= Build.VERSION_CODES.Q;
+
+ // Determine if the idmap for the current overlay can be generated in the last idmap
+ // create-multiple invocation.
+ IdmapInvocation invocation = null;
+ if (!idmapInvocations.isEmpty()) {
+ final IdmapInvocation last = idmapInvocations.get(idmapInvocations.size() - 1);
+ if (last.enforceOverlayable == enforceOverlayable
+ && last.policy.equals(overlay.parsedConfig.policy)) {
+ invocation = last;
+ }
+ }
+
+ if (invocation == null) {
+ invocation = new IdmapInvocation(enforceOverlayable, overlay.parsedConfig.policy);
+ idmapInvocations.add(invocation);
+ }
+
+ invocation.overlayPaths.add(overlay.parsedConfig.parsedInfo.path.getAbsolutePath());
+ }
+ return idmapInvocations;
+ }
+
+ /**
+ * Creates idmap files for immutable overlays targeting the framework packages. Currently the
+ * android package is the only preloaded system package. Only the zygote can invoke this method.
+ *
+ * @return the paths of the created idmap files
+ */
+ @NonNull
+ public String[] createImmutableFrameworkIdmapsInZygote() {
+ if (Process.myUid() != Process.ROOT_UID) {
+ throw new IllegalStateException("This method can only be called from the root process");
+ }
+
+ final String targetPath = "/system/framework/framework-res.apk";
+ final ArrayList<String> idmapPaths = new ArrayList<>();
+ final ArrayList<IdmapInvocation> idmapInvocations =
+ getImmutableFrameworkOverlayIdmapInvocations();
+
+ for (int i = 0, n = idmapInvocations.size(); i < n; i++) {
+ final IdmapInvocation invocation = idmapInvocations.get(i);
+ final String[] idmaps = createIdmap(targetPath,
+ invocation.overlayPaths.toArray(new String[0]),
+ new String[]{OverlayConfigParser.OverlayPartition.POLICY_PUBLIC,
+ invocation.policy},
+ invocation.enforceOverlayable);
+
+ if (idmaps == null) {
+ Log.w(TAG, "'idmap2 create-multiple' failed: no mutable=\"false\" overlays"
+ + " targeting \"android\" will be loaded");
+ return new String[0];
+ }
+
+ idmapPaths.addAll(Arrays.asList(idmaps));
+ }
+
+ return idmapPaths.toArray(new String[0]);
+ }
+
+ /**
+ * For each overlay APK, this creates the idmap file that allows the overlay to override the
+ * target package.
+ *
+ * @return the paths of the created idmap
+ */
+ private static native String[] createIdmap(@NonNull String targetPath,
+ @NonNull String[] overlayPath, @NonNull String[] policies, boolean enforceOverlayable);
+}
diff --git a/core/java/com/android/internal/content/om/OverlayConfigParser.java b/core/java/com/android/internal/content/om/OverlayConfigParser.java
new file mode 100644
index 0000000..139607f
--- /dev/null
+++ b/core/java/com/android/internal/content/om/OverlayConfigParser.java
@@ -0,0 +1,391 @@
+/*
+ * 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.internal.content.om;
+
+import static com.android.internal.content.om.OverlayConfig.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackagePartitions;
+import android.content.pm.PackagePartitions.SystemPartition;
+import android.os.FileUtils;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Responsible for parsing configurations of Runtime Resource Overlays that control mutability,
+ * default enable state, and priority. To configure an overlay, create or modify the file located
+ * at {@code partition}/overlay/config/config.xml where {@code partition} is the partition of the
+ * overlay to be configured. In order to be configured, an overlay must reside in the overlay
+ * directory of the partition in which the overlay is configured.
+ *
+ * @see #parseOverlay(File, XmlPullParser, OverlayScanner, ParsingContext)
+ * @see #parseMerge(File, XmlPullParser, OverlayScanner, ParsingContext)
+ **/
+final class OverlayConfigParser {
+
+ // Default values for overlay configurations.
+ static final boolean DEFAULT_ENABLED_STATE = false;
+ static final boolean DEFAULT_MUTABILITY = true;
+
+ // Maximum recursive depth of processing merge tags.
+ private static final int MAXIMUM_MERGE_DEPTH = 5;
+
+ // The subdirectory within a partition's overlay directory that contains the configuration files
+ // for the partition.
+ private static final String CONFIG_DIRECTORY = "config";
+
+ /**
+ * The name of the configuration file to parse for overlay configurations. This class does not
+ * scan for overlay configuration files within the {@link #CONFIG_DIRECTORY}; rather, other
+ * files can be included at a particular position within this file using the <merge> tag.
+ *
+ * @see #parseMerge(File, XmlPullParser, OverlayScanner, ParsingContext)
+ */
+ private static final String CONFIG_DEFAULT_FILENAME = CONFIG_DIRECTORY + "/config.xml";
+
+ /** Represents the configurations of a particular overlay. */
+ public static class ParsedConfiguration {
+ @NonNull
+ public final String packageName;
+
+ /** Whether or not the overlay is enabled by default. */
+ public final boolean enabled;
+
+ /**
+ * Whether or not the overlay is mutable and can have its enabled state changed dynamically
+ * using the {@code OverlayManagerService}.
+ **/
+ public final boolean mutable;
+
+ /** The policy granted to overlays on the partition in which the overlay is located. */
+ @NonNull
+ public final String policy;
+
+ /** Information extracted from the manifest of the overlay. */
+ @NonNull
+ public final ParsedOverlayInfo parsedInfo;
+
+ ParsedConfiguration(@NonNull String packageName, boolean enabled, boolean mutable,
+ @NonNull String policy, @NonNull ParsedOverlayInfo parsedInfo) {
+ this.packageName = packageName;
+ this.enabled = enabled;
+ this.mutable = mutable;
+ this.policy = policy;
+ this.parsedInfo = parsedInfo;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + String.format("{packageName=%s, enabled=%s"
+ + ", mutable=%s, policy=%s, parsedInfo=%s}", packageName, enabled,
+ mutable, policy, parsedInfo);
+ }
+ }
+
+ static class OverlayPartition extends SystemPartition {
+ // Policies passed to idmap2 during idmap creation.
+ // Keep partition policy constants in sync with f/b/cmds/idmap2/include/idmap2/Policies.h.
+ static final String POLICY_ODM = "odm";
+ static final String POLICY_OEM = "oem";
+ static final String POLICY_PRODUCT = "product";
+ static final String POLICY_PUBLIC = "public";
+ static final String POLICY_SYSTEM = "system";
+ static final String POLICY_VENDOR = "vendor";
+
+ @NonNull
+ public final String policy;
+
+ OverlayPartition(@NonNull SystemPartition partition) {
+ super(partition);
+ this.policy = policyForPartition(partition);
+ }
+
+ /**
+ * Creates a partition containing the same folders as the original partition but with a
+ * different root folder.
+ */
+ OverlayPartition(@NonNull File folder, @NonNull SystemPartition original) {
+ super(folder, original);
+ this.policy = policyForPartition(original);
+ }
+
+ private static String policyForPartition(SystemPartition partition) {
+ switch (partition.type) {
+ case PackagePartitions.PARTITION_SYSTEM:
+ case PackagePartitions.PARTITION_SYSTEM_EXT:
+ return POLICY_SYSTEM;
+ case PackagePartitions.PARTITION_VENDOR:
+ return POLICY_VENDOR;
+ case PackagePartitions.PARTITION_ODM:
+ return POLICY_ODM;
+ case PackagePartitions.PARTITION_OEM:
+ return POLICY_OEM;
+ case PackagePartitions.PARTITION_PRODUCT:
+ return POLICY_PRODUCT;
+ default:
+ throw new IllegalStateException("Unable to determine policy for "
+ + partition.folder);
+ }
+ }
+ }
+
+ /** This class holds state related to parsing the configurations of a partition. */
+ private static class ParsingContext {
+ // The overlay directory of the partition
+ private final OverlayPartition mPartition;
+
+ // The ordered list of configured overlays
+ private final ArrayList<ParsedConfiguration> mOrderedConfigurations = new ArrayList<>();
+
+ // The packages configured in the partition
+ private final ArraySet<String> mConfiguredOverlays = new ArraySet<>();
+
+ // Whether an mutable overlay has been configured in the partition
+ private boolean mFoundMutableOverlay;
+
+ // The current recursive depth of merging configuration files
+ private int mMergeDepth;
+
+ private ParsingContext(OverlayPartition partition) {
+ mPartition = partition;
+ }
+ }
+
+ /**
+ * Retrieves overlays configured within the partition in increasing priority order.
+ *
+ * If {@code scanner} is null, then the {@link ParsedConfiguration#parsedInfo} fields of the
+ * added configured overlays will be null and the parsing logic will not assert that the
+ * configured overlays exist within the partition.
+ *
+ * @return list of configured overlays if configuration file exists; otherwise, null
+ */
+ @Nullable
+ static ArrayList<ParsedConfiguration> getConfigurations(
+ @NonNull OverlayPartition partition, @Nullable OverlayScanner scanner) {
+ if (partition.getOverlayFolder() == null) {
+ return null;
+ }
+
+ if (scanner != null) {
+ scanner.scanDir(partition.getOverlayFolder());
+ }
+
+ final File configFile = new File(partition.getOverlayFolder(), CONFIG_DEFAULT_FILENAME);
+ if (!configFile.exists()) {
+ return null;
+ }
+
+ final ParsingContext parsingContext = new ParsingContext(partition);
+ readConfigFile(configFile, scanner, parsingContext);
+ return parsingContext.mOrderedConfigurations;
+ }
+
+ private static void readConfigFile(@NonNull File configFile, @Nullable OverlayScanner scanner,
+ @NonNull ParsingContext parsingContext) {
+ FileReader configReader;
+ try {
+ configReader = new FileReader(configFile);
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "Couldn't find or open overlay configuration file " + configFile);
+ return;
+ }
+
+ try {
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(configReader);
+ XmlUtils.beginDocument(parser, "config");
+
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ final String name = parser.getName();
+ switch (name) {
+ case "merge":
+ parseMerge(configFile, parser, scanner, parsingContext);
+ break;
+ case "overlay":
+ parseOverlay(configFile, parser, scanner, parsingContext);
+ break;
+ default:
+ Log.w(TAG, String.format("Tag %s is unknown in %s at %s",
+ name, configFile, parser.getPositionDescription()));
+ break;
+ }
+ }
+ } catch (XmlPullParserException | IOException e) {
+ Log.w(TAG, "Got exception parsing overlay configuration.", e);
+ } finally {
+ IoUtils.closeQuietly(configReader);
+ }
+ }
+
+ /**
+ * Parses a <merge> tag within an overlay configuration file.
+ *
+ * Merge tags allow for other configuration files to be "merged" at the current parsing
+ * position into the current configuration file being parsed. The {@code path} attribute of the
+ * tag represents the path of the file to merge relative to the directory containing overlay
+ * configuration files.
+ */
+ private static void parseMerge(@NonNull File configFile, @NonNull XmlPullParser parser,
+ @Nullable OverlayScanner scanner, @NonNull ParsingContext parsingContext) {
+ final String path = parser.getAttributeValue(null, "path");
+ if (path == null) {
+ throw new IllegalStateException(String.format("<merge> without path in %s at %s"
+ + configFile, parser.getPositionDescription()));
+ }
+
+ if (path.startsWith("/")) {
+ throw new IllegalStateException(String.format(
+ "Path %s must be relative to the directory containing overlay configurations "
+ + " files in %s at %s ", path, configFile,
+ parser.getPositionDescription()));
+ }
+
+ if (parsingContext.mMergeDepth++ == MAXIMUM_MERGE_DEPTH) {
+ throw new IllegalStateException(String.format(
+ "Maximum <merge> depth exceeded in %s at %s", configFile,
+ parser.getPositionDescription()));
+ }
+
+ final File configDirectory;
+ final File includedConfigFile;
+ try {
+ configDirectory = new File(parsingContext.mPartition.getOverlayFolder(),
+ CONFIG_DIRECTORY).getCanonicalFile();
+ includedConfigFile = new File(configDirectory, path).getCanonicalFile();
+ } catch (IOException e) {
+ throw new IllegalStateException(
+ String.format("Couldn't find or open merged configuration file %s in %s at %s",
+ path, configFile, parser.getPositionDescription()), e);
+ }
+
+ if (!includedConfigFile.exists()) {
+ throw new IllegalStateException(
+ String.format("Merged configuration file %s does not exist in %s at %s",
+ path, configFile, parser.getPositionDescription()));
+ }
+
+ if (!FileUtils.contains(configDirectory, includedConfigFile)) {
+ throw new IllegalStateException(
+ String.format(
+ "Merged file %s outside of configuration directory in %s at %s",
+ includedConfigFile.getAbsolutePath(), includedConfigFile,
+ parser.getPositionDescription()));
+ }
+
+ readConfigFile(includedConfigFile, scanner, parsingContext);
+ parsingContext.mMergeDepth--;
+ }
+
+ /**
+ * Parses an <overlay> tag within an overlay configuration file.
+ *
+ * Requires a {@code package} attribute that indicates which package is being configured.
+ * The optional {@code enabled} attribute controls whether or not the overlay is enabled by
+ * default (default is false). The optional {@code mutable} attribute controls whether or
+ * not the overlay is mutable and can have its enabled state changed at runtime (default is
+ * true).
+ *
+ * The order in which overlays that override the same resources are configured matters. An
+ * overlay will have a greater priority than overlays with configurations preceding its own
+ * configuration.
+ *
+ * Configurations of immutable overlays must precede configurations of mutable overlays.
+ * An overlay cannot be configured in multiple locations. All configured overlay must exist
+ * within the partition of the configuration file. An overlay cannot be configured multiple
+ * times in a single partition.
+ *
+ * Overlays not listed within a configuration file will be mutable and disabled by default. The
+ * order of non-configured overlays when enabled by the OverlayManagerService is undefined.
+ */
+ private static void parseOverlay(@NonNull File configFile, @NonNull XmlPullParser parser,
+ @Nullable OverlayScanner scanner, @NonNull ParsingContext parsingContext) {
+ final String packageName = parser.getAttributeValue(null, "package");
+ if (packageName == null) {
+ throw new IllegalStateException(String.format("\"<overlay> without package in %s at %s",
+ configFile, parser.getPositionDescription()));
+ }
+
+ // Ensure the overlay being configured is present in the partition during zygote
+ // initialization.
+ ParsedOverlayInfo info = null;
+ if (scanner != null) {
+ info = scanner.getParsedInfo(packageName);
+ if (info == null|| !parsingContext.mPartition.containsOverlay(info.path)) {
+ throw new IllegalStateException(
+ String.format("overlay %s not present in partition %s in %s at %s",
+ packageName, parsingContext.mPartition.getOverlayFolder(),
+ configFile, parser.getPositionDescription()));
+ }
+ }
+
+ if (parsingContext.mConfiguredOverlays.contains(packageName)) {
+ throw new IllegalStateException(
+ String.format("overlay %s configured multiple times in a single partition"
+ + " in %s at %s", packageName, configFile,
+ parser.getPositionDescription()));
+ }
+
+ boolean isEnabled = DEFAULT_ENABLED_STATE;
+ final String enabled = parser.getAttributeValue(null, "enabled");
+ if (enabled != null) {
+ isEnabled = !"false".equals(enabled);
+ }
+
+ boolean isMutable = DEFAULT_MUTABILITY;
+ final String mutable = parser.getAttributeValue(null, "mutable");
+ if (mutable != null) {
+ isMutable = !"false".equals(mutable);
+ if (!isMutable && parsingContext.mFoundMutableOverlay) {
+ throw new IllegalStateException(String.format(
+ "immutable overlays must precede mutable overlays:"
+ + " found in %s at %s",
+ configFile, parser.getPositionDescription()));
+ }
+ }
+
+ if (isMutable) {
+ parsingContext.mFoundMutableOverlay = true;
+ } else if (!isEnabled) {
+ // Default disabled, immutable overlays may be a misconfiguration of the system so warn
+ // developers.
+ Log.w(TAG, "found default-disabled immutable overlay " + packageName);
+ }
+
+ final ParsedConfiguration Config = new ParsedConfiguration(packageName, isEnabled,
+ isMutable, parsingContext.mPartition.policy, info);
+ parsingContext.mConfiguredOverlays.add(packageName);
+ parsingContext.mOrderedConfigurations.add(Config);
+ }
+}
diff --git a/core/java/com/android/internal/content/om/OverlayScanner.java b/core/java/com/android/internal/content/om/OverlayScanner.java
new file mode 100644
index 0000000..a85cf56
--- /dev/null
+++ b/core/java/com/android/internal/content/om/OverlayScanner.java
@@ -0,0 +1,138 @@
+/*
+ * 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.internal.content.om;
+
+import static com.android.internal.content.om.OverlayConfig.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.util.Collection;
+
+/**
+ * This class scans a directory containing overlay APKs and extracts information from the overlay
+ * manifests by parsing the overlay manifests.
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class OverlayScanner {
+
+ /** Represents information parsed from the manifest of an overlay. */
+ public static class ParsedOverlayInfo {
+ public final String packageName;
+ public final String targetPackageName;
+ public final int targetSdkVersion;
+ public final boolean isStatic;
+ public final int priority;
+ public final File path;
+
+ public ParsedOverlayInfo(String packageName, String targetPackageName,
+ int targetSdkVersion, boolean isStatic, int priority, File path) {
+ this.packageName = packageName;
+ this.targetPackageName = targetPackageName;
+ this.targetSdkVersion = targetSdkVersion;
+ this.isStatic = isStatic;
+ this.priority = priority;
+ this.path = path;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + String.format("{packageName=%s"
+ + ", targetPackageName=%s, targetSdkVersion=%s, isStatic=%s"
+ + ", priority=%s, path=%s}",
+ packageName, targetPackageName, targetSdkVersion, isStatic, priority, path);
+ }
+ }
+
+ /**
+ * A map of overlay package name to the parsed manifest information of the latest version of
+ * the overlay.
+ */
+ private final ArrayMap<String, ParsedOverlayInfo> mParsedOverlayInfos = new ArrayMap<>();
+
+ /** Retrieves information parsed from the overlay with the package name. */
+ @Nullable
+ public final ParsedOverlayInfo getParsedInfo(String packageName) {
+ return mParsedOverlayInfos.get(packageName);
+ }
+
+ /** Retrieves all of the scanned overlays. */
+ @NonNull
+ final Collection<ParsedOverlayInfo> getAllParsedInfos() {
+ return mParsedOverlayInfos.values();
+ }
+
+ /**
+ * Recursively searches the directory for overlay APKs. If an overlay is found with the same
+ * package name as a previously scanned overlay, the info of the new overlay will replace the
+ * info of the previously scanned overlay.
+ */
+ public void scanDir(File partitionOverlayDir) {
+ if (!partitionOverlayDir.exists() || !partitionOverlayDir.isDirectory()) {
+ return;
+ }
+
+ if (!partitionOverlayDir.canRead()) {
+ Log.w(TAG, "Directory " + partitionOverlayDir + " cannot be read");
+ return;
+ }
+
+ final File[] files = partitionOverlayDir.listFiles();
+ if (files == null) {
+ return;
+ }
+
+ for (int i = 0; i < files.length; i++) {
+ final File f = files[i];
+ if (f.isDirectory()) {
+ scanDir(f);
+ }
+
+ if (!f.isFile() || !f.getPath().endsWith(".apk")) {
+ continue;
+ }
+
+ final ParsedOverlayInfo info = parseOverlayManifest(f);
+ if (info == null) {
+ continue;
+ }
+
+ mParsedOverlayInfos.put(info.packageName, info);
+ }
+ }
+
+ /** Extracts information about the overlay from its manifest. */
+ @VisibleForTesting
+ public ParsedOverlayInfo parseOverlayManifest(File overlayApk) {
+ try {
+ final PackageParser.ApkLite apkLite = PackageParser.parseApkLite(overlayApk, 0);
+ return apkLite.targetPackageName == null ? null :
+ new ParsedOverlayInfo(apkLite.packageName, apkLite.targetPackageName,
+ apkLite.targetSdkVersion, apkLite.overlayIsStatic,
+ apkLite.overlayPriority, new File(apkLite.codePath));
+ } catch (PackageParser.PackageParserException e) {
+ Log.w(TAG, "Got exception loading overlay.", e);
+ return null;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java
new file mode 100644
index 0000000..26f81d9
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java
@@ -0,0 +1,202 @@
+/*
+ * 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.internal.os;
+
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Reads cpu time bpf maps.
+ *
+ * It is implemented as singletons for each separate set of per-UID times. Get___Instance() method
+ * returns the corresponding reader instance. In order to prevent frequent GC, it reuses the same
+ * SparseArray to store data read from BPF maps.
+ *
+ * A KernelCpuUidBpfMapReader instance keeps an error counter. When the number of read errors within
+ * that instance accumulates to 5, this instance will reject all further read requests.
+ *
+ * Data fetched within last 500ms is considered fresh, since the reading lifecycle can take up to
+ * 25ms. KernelCpuUidBpfMapReader always tries to use cache if it is fresh and valid, but it can
+ * be disabled through a parameter.
+ *
+ * A KernelCpuUidBpfMapReader instance is thread-safe. It acquires a write lock when reading the bpf
+ * map, releases it right after, then acquires a read lock before returning a BpfMapIterator. Caller
+ * is responsible for closing BpfMapIterator (also auto-closable) after reading, otherwise deadlock
+ * will occur.
+ */
+public abstract class KernelCpuUidBpfMapReader {
+ private static final int ERROR_THRESHOLD = 5;
+ private static final long FRESHNESS_MS = 500L;
+
+ private static final KernelCpuUidBpfMapReader FREQ_TIME_READER =
+ new KernelCpuUidFreqTimeBpfMapReader();
+
+ private static final KernelCpuUidBpfMapReader ACTIVE_TIME_READER =
+ new KernelCpuUidActiveTimeBpfMapReader();
+
+ private static final KernelCpuUidBpfMapReader CLUSTER_TIME_READER =
+ new KernelCpuUidClusterTimeBpfMapReader();
+
+ static KernelCpuUidBpfMapReader getFreqTimeReaderInstance() {
+ return FREQ_TIME_READER;
+ }
+
+ static KernelCpuUidBpfMapReader getActiveTimeReaderInstance() {
+ return ACTIVE_TIME_READER;
+ }
+
+ static KernelCpuUidBpfMapReader getClusterTimeReaderInstance() {
+ return CLUSTER_TIME_READER;
+ }
+
+ final String mTag = this.getClass().getSimpleName();
+ private int mErrors = 0;
+ private boolean mTracking = false;
+ protected SparseArray<long[]> mData = new SparseArray<>();
+ private long mLastReadTime = 0;
+ protected final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
+ protected final ReentrantReadWriteLock.ReadLock mReadLock = mLock.readLock();
+ protected final ReentrantReadWriteLock.WriteLock mWriteLock = mLock.writeLock();
+
+ public native boolean startTrackingBpfTimes();
+
+ protected abstract boolean readBpfData();
+
+ /**
+ * Returns an array of metadata used to inform the caller of 1) the size of array required by
+ * getNextUid and 2) how to interpret the raw data copied to that array.
+ */
+ public abstract long[] getDataDimensions();
+
+ public void removeUidsInRange(int startUid, int endUid) {
+ if (mErrors > ERROR_THRESHOLD) {
+ return;
+ }
+ mWriteLock.lock();
+ int firstIndex = mData.indexOfKey(startUid);
+ int lastIndex = mData.indexOfKey(endUid);
+ mData.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+ mWriteLock.unlock();
+ }
+
+ public BpfMapIterator open() {
+ return open(false);
+ }
+
+ public BpfMapIterator open(boolean ignoreCache) {
+ if (mErrors > ERROR_THRESHOLD) {
+ return null;
+ }
+ if (!mTracking && !startTrackingBpfTimes()) {
+ Slog.w(mTag, "Failed to start tracking");
+ mErrors++;
+ return null;
+ }
+ if (ignoreCache) {
+ mWriteLock.lock();
+ } else {
+ mReadLock.lock();
+ if (dataValid()) {
+ return new BpfMapIterator();
+ }
+ mReadLock.unlock();
+ mWriteLock.lock();
+ if (dataValid()) {
+ mReadLock.lock();
+ mWriteLock.unlock();
+ return new BpfMapIterator();
+ }
+ }
+ if (readBpfData()) {
+ mLastReadTime = SystemClock.elapsedRealtime();
+ mReadLock.lock();
+ mWriteLock.unlock();
+ return new BpfMapIterator();
+ }
+
+ mWriteLock.unlock();
+ mErrors++;
+ Slog.w(mTag, "Failed to read bpf times");
+ return null;
+ }
+
+ private boolean dataValid() {
+ return mData.size() > 0 && (SystemClock.elapsedRealtime() - mLastReadTime < FRESHNESS_MS);
+ }
+
+ public class BpfMapIterator implements AutoCloseable {
+ private int mPos;
+
+ public BpfMapIterator() {
+ };
+
+ public boolean getNextUid(long[] buf) {
+ if (mPos >= mData.size()) {
+ return false;
+ }
+ buf[0] = mData.keyAt(mPos);
+ System.arraycopy(mData.valueAt(mPos), 0, buf, 1, mData.valueAt(mPos).length);
+ mPos++;
+ return true;
+ }
+
+ public void close() {
+ mReadLock.unlock();
+ }
+ }
+
+ public static class KernelCpuUidFreqTimeBpfMapReader extends KernelCpuUidBpfMapReader {
+
+ private final native boolean removeUidRange(int startUid, int endUid);
+
+ @Override
+ protected final native boolean readBpfData();
+
+ @Override
+ public final native long[] getDataDimensions();
+
+ @Override
+ public void removeUidsInRange(int startUid, int endUid) {
+ mWriteLock.lock();
+ super.removeUidsInRange(startUid, endUid);
+ removeUidRange(startUid, endUid);
+ mWriteLock.unlock();
+ }
+ }
+
+ public static class KernelCpuUidActiveTimeBpfMapReader extends KernelCpuUidBpfMapReader {
+
+ @Override
+ protected final native boolean readBpfData();
+
+ @Override
+ public final native long[] getDataDimensions();
+ }
+
+ public static class KernelCpuUidClusterTimeBpfMapReader extends KernelCpuUidBpfMapReader {
+
+ @Override
+ protected final native boolean readBpfData();
+
+ @Override
+ public final native long[] getDataDimensions();
+ }
+}
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
index f1eb2fb..f7fad2c 100644
--- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -28,6 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
+import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;
import java.io.BufferedReader;
import java.io.FileWriter;
@@ -57,6 +58,8 @@
final SparseArray<T> mLastTimes = new SparseArray<>();
final KernelCpuProcStringReader mReader;
final boolean mThrottle;
+ protected boolean mBpfTimesAvailable;
+ final KernelCpuUidBpfMapReader mBpfReader;
private long mMinTimeBetweenRead = DEFAULT_MIN_TIME_BETWEEN_READ;
private long mLastReadTimeMs = 0;
@@ -73,9 +76,15 @@
void onUidCpuTime(int uid, T time);
}
- KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ KernelCpuUidTimeReader(KernelCpuProcStringReader reader, @Nullable KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
mReader = reader;
mThrottle = throttle;
+ mBpfReader = bpfReader;
+ mBpfTimesAvailable = (mBpfReader != null);
+ }
+
+ KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ this(reader, null, throttle);
}
/**
@@ -151,9 +160,13 @@
}
mLastTimes.put(startUid, null);
mLastTimes.put(endUid, null);
- final int firstIndex = mLastTimes.indexOfKey(startUid);
- final int lastIndex = mLastTimes.indexOfKey(endUid);
+ int firstIndex = mLastTimes.indexOfKey(startUid);
+ int lastIndex = mLastTimes.indexOfKey(endUid);
mLastTimes.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+
+ if (mBpfTimesAvailable) {
+ mBpfReader.removeUidsInRange(startUid, endUid);
+ }
}
/**
@@ -323,13 +336,13 @@
public KernelCpuUidFreqTimeReader(boolean throttle) {
this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
- throttle);
+ KernelCpuUidBpfMapReader.getFreqTimeReaderInstance(), throttle);
}
@VisibleForTesting
public KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
- boolean throttle) {
- super(reader, throttle);
+ KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
+ super(reader, bpfReader, throttle);
mProcFilePath = Paths.get(procFile);
}
@@ -370,19 +383,24 @@
if (!mAllUidTimesAvailable) {
return null;
}
- final int oldMask = StrictMode.allowThreadDiskReadsMask();
- try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
- if (readFreqs(reader.readLine()) == null) {
+ if (mBpfTimesAvailable) {
+ readFreqsThroughBpf();
+ }
+ if (mCpuFreqs == null) {
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
+ if (readFreqs(reader.readLine()) == null) {
+ return null;
+ }
+ } catch (IOException e) {
+ if (++mErrors >= MAX_ERROR_COUNT) {
+ mAllUidTimesAvailable = false;
+ }
+ Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
return null;
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
}
- } catch (IOException e) {
- if (++mErrors >= MAX_ERROR_COUNT) {
- mAllUidTimesAvailable = false;
- }
- Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
- return null;
- } finally {
- StrictMode.setThreadPolicyMask(oldMask);
}
// Check if the freqs in the proc file correspond to per-cluster freqs.
final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
@@ -402,6 +420,21 @@
return mCpuFreqs;
}
+ private long[] readFreqsThroughBpf() {
+ if (!mBpfTimesAvailable || mBpfReader == null) {
+ return null;
+ }
+ mCpuFreqs = mBpfReader.getDataDimensions();
+ if (mCpuFreqs == null) {
+ return null;
+ }
+ mFreqCount = mCpuFreqs.length;
+ mCurTimes = new long[mFreqCount];
+ mDeltaTimes = new long[mFreqCount];
+ mBuffer = new long[mFreqCount + 1];
+ return mCpuFreqs;
+ }
+
private long[] readFreqs(String line) {
if (line == null || line.trim().isEmpty()) {
return null;
@@ -422,8 +455,45 @@
return mCpuFreqs;
}
+ private void processUidDelta(@Nullable Callback<long[]> cb) {
+ final int uid = (int) mBuffer[0];
+ long[] lastTimes = mLastTimes.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[mFreqCount];
+ mLastTimes.put(uid, lastTimes);
+ }
+ copyToCurTimes();
+ boolean notify = false;
+ boolean valid = true;
+ for (int i = 0; i < mFreqCount; i++) {
+ // Unit is 10ms.
+ mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
+ if (mDeltaTimes[i] < 0) {
+ Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
+ valid = false;
+ }
+ notify |= mDeltaTimes[i] > 0;
+ }
+ if (notify && valid) {
+ System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, mDeltaTimes);
+ }
+ }
+ }
+
@Override
void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ if (mBpfTimesAvailable) {
+ try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+ if (checkPrecondition(iter)) {
+ while (iter.getNextUid(mBuffer)) {
+ processUidDelta(cb);
+ }
+ return;
+ }
+ }
+ }
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
if (!checkPrecondition(iter)) {
return;
@@ -434,36 +504,24 @@
Slog.wtf(mTag, "Invalid line: " + buf.toString());
continue;
}
- final int uid = (int) mBuffer[0];
- long[] lastTimes = mLastTimes.get(uid);
- if (lastTimes == null) {
- lastTimes = new long[mFreqCount];
- mLastTimes.put(uid, lastTimes);
- }
- copyToCurTimes();
- boolean notify = false;
- boolean valid = true;
- for (int i = 0; i < mFreqCount; i++) {
- // Unit is 10ms.
- mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
- if (mDeltaTimes[i] < 0) {
- Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
- valid = false;
- }
- notify |= mDeltaTimes[i] > 0;
- }
- if (notify && valid) {
- System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
- if (cb != null) {
- cb.onUidCpuTime(uid, mDeltaTimes);
- }
- }
+ processUidDelta(cb);
}
}
}
@Override
void readAbsoluteImpl(Callback<long[]> cb) {
+ if (mBpfTimesAvailable) {
+ try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+ if (checkPrecondition(iter)) {
+ while (iter.getNextUid(mBuffer)) {
+ copyToCurTimes();
+ cb.onUidCpuTime((int) mBuffer[0], mCurTimes);
+ }
+ return;
+ }
+ }
+ }
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
if (!checkPrecondition(iter)) {
return;
@@ -481,11 +539,24 @@
}
private void copyToCurTimes() {
+ long factor = mBpfTimesAvailable ? 1 : 10;
for (int i = 0; i < mFreqCount; i++) {
- mCurTimes[i] = mBuffer[i + 1] * 10;
+ mCurTimes[i] = mBuffer[i + 1] * factor;
}
}
+ private boolean checkPrecondition(BpfMapIterator iter) {
+ if (iter == null) {
+ mBpfTimesAvailable = false;
+ return false;
+ }
+ if (mCpuFreqs != null) {
+ return true;
+ }
+ mBpfTimesAvailable = (readFreqsThroughBpf() != null);
+ return mBpfTimesAvailable;
+ }
+
private boolean checkPrecondition(ProcFileIterator iter) {
if (iter == null || !iter.hasNextLine()) {
// Error logged in KernelCpuProcStringReader.
@@ -544,16 +615,43 @@
private long[] mBuffer;
public KernelCpuUidActiveTimeReader(boolean throttle) {
- super(KernelCpuProcStringReader.getActiveTimeReaderInstance(), throttle);
+ super(KernelCpuProcStringReader.getActiveTimeReaderInstance(),
+ KernelCpuUidBpfMapReader.getActiveTimeReaderInstance(), throttle);
}
@VisibleForTesting
- public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
- super(reader, throttle);
+ public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
+ super(reader, bpfReader, throttle);
+ }
+
+ private void processUidDelta(@Nullable Callback<Long> cb) {
+ int uid = (int) mBuffer[0];
+ long cpuActiveTime = sumActiveTime(mBuffer, mBpfTimesAvailable ? 1 : 10);
+ if (cpuActiveTime > 0) {
+ long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
+ if (delta > 0) {
+ mLastTimes.put(uid, cpuActiveTime);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, delta);
+ }
+ } else if (delta < 0) {
+ Slog.e(mTag, "Negative delta from active time proc: " + delta);
+ }
+ }
}
@Override
void readDeltaImpl(@Nullable Callback<Long> cb) {
+ if (mBpfTimesAvailable) {
+ try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+ if (checkPrecondition(iter)) {
+ while (iter.getNextUid(mBuffer)) {
+ processUidDelta(cb);
+ }
+ return;
+ }
+ }
+ }
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
if (!checkPrecondition(iter)) {
return;
@@ -564,25 +662,30 @@
Slog.wtf(mTag, "Invalid line: " + buf.toString());
continue;
}
- int uid = (int) mBuffer[0];
- long cpuActiveTime = sumActiveTime(mBuffer);
- if (cpuActiveTime > 0) {
- long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
- if (delta > 0) {
- mLastTimes.put(uid, cpuActiveTime);
- if (cb != null) {
- cb.onUidCpuTime(uid, delta);
- }
- } else if (delta < 0) {
- Slog.e(mTag, "Negative delta from active time proc: " + delta);
- }
- }
+ processUidDelta(cb);
}
}
}
+ private void processUidAbsolute(@Nullable Callback<Long> cb) {
+ long cpuActiveTime = sumActiveTime(mBuffer, mBpfTimesAvailable ? 1 : 10);
+ if (cpuActiveTime > 0) {
+ cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
+ }
+ }
+
@Override
void readAbsoluteImpl(Callback<Long> cb) {
+ if (mBpfTimesAvailable) {
+ try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+ if (checkPrecondition(iter)) {
+ while (iter.getNextUid(mBuffer)) {
+ processUidAbsolute(cb);
+ }
+ return;
+ }
+ }
+ }
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
if (!checkPrecondition(iter)) {
return;
@@ -593,23 +696,38 @@
Slog.wtf(mTag, "Invalid line: " + buf.toString());
continue;
}
- long cpuActiveTime = sumActiveTime(mBuffer);
- if (cpuActiveTime > 0) {
- cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
- }
+ processUidAbsolute(cb);
}
}
}
- private static long sumActiveTime(long[] times) {
+ private static long sumActiveTime(long[] times, double factor) {
// UID is stored at times[0].
double sum = 0;
for (int i = 1; i < times.length; i++) {
- sum += (double) times[i] * 10 / i; // Unit is 10ms.
+ sum += (double) times[i] * factor / i; // Unit is 10ms.
}
return (long) sum;
}
+ private boolean checkPrecondition(BpfMapIterator iter) {
+ if (iter == null) {
+ mBpfTimesAvailable = false;
+ return false;
+ }
+ if (mCores > 0) {
+ return true;
+ }
+ long[] cores = mBpfReader.getDataDimensions();
+ if (cores == null || cores.length < 1) {
+ mBpfTimesAvailable = false;
+ return false;
+ }
+ mCores = (int) cores[0];
+ mBuffer = new long[mCores + 1];
+ return true;
+ }
+
private boolean checkPrecondition(ProcFileIterator iter) {
if (iter == null || !iter.hasNextLine()) {
// Error logged in KernelCpuProcStringReader.
@@ -668,16 +786,54 @@
private long[] mDeltaTime;
public KernelCpuUidClusterTimeReader(boolean throttle) {
- super(KernelCpuProcStringReader.getClusterTimeReaderInstance(), throttle);
+ super(KernelCpuProcStringReader.getClusterTimeReaderInstance(),
+ KernelCpuUidBpfMapReader.getClusterTimeReaderInstance(), throttle);
}
@VisibleForTesting
- public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
- super(reader, throttle);
+ public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader,
+ KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
+ super(reader, bpfReader, throttle);
+ }
+
+ void processUidDelta(@Nullable Callback<long[]> cb) {
+ int uid = (int) mBuffer[0];
+ long[] lastTimes = mLastTimes.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[mNumClusters];
+ mLastTimes.put(uid, lastTimes);
+ }
+ sumClusterTime();
+ boolean valid = true;
+ boolean notify = false;
+ for (int i = 0; i < mNumClusters; i++) {
+ mDeltaTime[i] = mCurTime[i] - lastTimes[i];
+ if (mDeltaTime[i] < 0) {
+ Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
+ valid = false;
+ }
+ notify |= mDeltaTime[i] > 0;
+ }
+ if (notify && valid) {
+ System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, mDeltaTime);
+ }
+ }
}
@Override
void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ if (mBpfTimesAvailable) {
+ try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+ if (checkPrecondition(iter)) {
+ while (iter.getNextUid(mBuffer)) {
+ processUidDelta(cb);
+ }
+ return;
+ }
+ }
+ }
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
if (!checkPrecondition(iter)) {
return;
@@ -688,35 +844,24 @@
Slog.wtf(mTag, "Invalid line: " + buf.toString());
continue;
}
- int uid = (int) mBuffer[0];
- long[] lastTimes = mLastTimes.get(uid);
- if (lastTimes == null) {
- lastTimes = new long[mNumClusters];
- mLastTimes.put(uid, lastTimes);
- }
- sumClusterTime();
- boolean valid = true;
- boolean notify = false;
- for (int i = 0; i < mNumClusters; i++) {
- mDeltaTime[i] = mCurTime[i] - lastTimes[i];
- if (mDeltaTime[i] < 0) {
- Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
- valid = false;
- }
- notify |= mDeltaTime[i] > 0;
- }
- if (notify && valid) {
- System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
- if (cb != null) {
- cb.onUidCpuTime(uid, mDeltaTime);
- }
- }
+ processUidDelta(cb);
}
}
}
@Override
void readAbsoluteImpl(Callback<long[]> cb) {
+ if (mBpfTimesAvailable) {
+ try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+ if (checkPrecondition(iter)) {
+ while (iter.getNextUid(mBuffer)) {
+ sumClusterTime();
+ cb.onUidCpuTime((int) mBuffer[0], mCurTime);
+ }
+ return;
+ }
+ }
+ }
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
if (!checkPrecondition(iter)) {
return;
@@ -734,17 +879,45 @@
}
private void sumClusterTime() {
+ double factor = mBpfTimesAvailable ? 1 : 10;
// UID is stored at mBuffer[0].
int core = 1;
for (int i = 0; i < mNumClusters; i++) {
double sum = 0;
for (int j = 1; j <= mCoresOnClusters[i]; j++) {
- sum += (double) mBuffer[core++] * 10 / j; // Unit is 10ms.
+ sum += (double) mBuffer[core++] * factor / j; // Unit is 10ms.
}
mCurTime[i] = (long) sum;
}
}
+ private boolean checkPrecondition(BpfMapIterator iter) {
+ if (iter == null) {
+ mBpfTimesAvailable = false;
+ return false;
+ }
+ if (mNumClusters > 0) {
+ return true;
+ }
+ long[] coresOnClusters = mBpfReader.getDataDimensions();
+ if (coresOnClusters == null || coresOnClusters.length < 1) {
+ mBpfTimesAvailable = false;
+ return false;
+ }
+ mNumClusters = coresOnClusters.length;
+ mCoresOnClusters = new int[mNumClusters];
+ int cores = 0;
+ for (int i = 0; i < mNumClusters; i++) {
+ mCoresOnClusters[i] = (int) coresOnClusters[i];
+ cores += mCoresOnClusters[i];
+ }
+ mNumCores = cores;
+ mBuffer = new long[cores + 1];
+ mCurTime = new long[mNumClusters];
+ mDeltaTime = new long[mNumClusters];
+ return true;
+ }
+
private boolean checkPrecondition(ProcFileIterator iter) {
if (iter == null || !iter.hasNextLine()) {
// Error logged in KernelCpuProcStringReader.
diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
index 3c43a11..fc0ce6f 100644
--- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
@@ -53,6 +53,8 @@
private int mReadErrorCounter;
@GuardedBy("this")
private boolean mSingleUidCpuTimesAvailable = true;
+ @GuardedBy("this")
+ private boolean mBpfTimesAvailable = true;
// We use the freq count obtained from /proc/uid_time_in_state to decide how many longs
// to read from each /proc/uid/<uid>/time_in_state. On the first read, verify if this is
// correct and if not, set {@link #mSingleUidCpuTimesAvailable} to false. This flag will
@@ -62,6 +64,8 @@
private final Injector mInjector;
+ private static final native boolean canReadBpfTimes();
+
KernelSingleUidTimeReader(int cpuFreqsCount) {
this(cpuFreqsCount, new Injector());
}
@@ -83,6 +87,18 @@
if (!mSingleUidCpuTimesAvailable) {
return null;
}
+ if (mBpfTimesAvailable) {
+ final long[] cpuTimesMs = mInjector.readBpfData(uid);
+ if (cpuTimesMs.length == 0) {
+ mBpfTimesAvailable = false;
+ } else if (!mCpuFreqsCountVerified && cpuTimesMs.length != mCpuFreqsCount) {
+ mSingleUidCpuTimesAvailable = false;
+ return null;
+ } else {
+ mCpuFreqsCountVerified = true;
+ return computeDelta(uid, cpuTimesMs);
+ }
+ }
// Read total cpu times from the proc file.
final String procFile = new StringBuilder(PROC_FILE_DIR)
.append(uid)
@@ -230,6 +246,8 @@
public byte[] readData(String procFile) throws IOException {
return Files.readAllBytes(Paths.get(procFile));
}
+
+ public native long[] readBpfData(int uid);
}
@VisibleForTesting
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 775368b..24222d3 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -335,7 +335,7 @@
super(context);
mLayoutInflater = LayoutInflater.from(context);
mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(),
- DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 0) != 0;
+ DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0;
}
/**
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 578c0cc..3378c07 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -1160,6 +1160,10 @@
addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY, 0);
}
+ if (PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT) {
+ addFeature(PackageManager.FEATURE_APP_ENUMERATION, 0);
+ }
+
for (String featureName : mUnavailableFeatures) {
removeFeature(featureName);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 900445c..253ecdc 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -197,8 +197,11 @@
"android_content_res_ObbScanner.cpp",
"android_content_res_Configuration.cpp",
"android_security_Scrypt.cpp",
+ "com_android_internal_content_om_OverlayConfig.cpp",
"com_android_internal_os_ClassLoaderFactory.cpp",
"com_android_internal_os_FuseAppLoop.cpp",
+ "com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
+ "com_android_internal_os_KernelSingleUidTimeReader.cpp",
"com_android_internal_os_Zygote.cpp",
"com_android_internal_os_ZygoteInit.cpp",
"hwbinder/EphemeralStorage.cpp",
@@ -273,6 +276,7 @@
"libdl",
"libdl_android",
"libstatslog",
+ "libtimeinstate",
"server_configurable_flags",
"libstatspull",
],
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index d790ada..b711186 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -186,8 +186,11 @@
extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
extern int register_android_security_Scrypt(JNIEnv *env);
extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
+extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env);
extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
+extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env);
+extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env);
extern int register_com_android_internal_os_Zygote(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
@@ -1497,6 +1500,7 @@
REG_JNI(register_android_os_MemoryFile),
REG_JNI(register_android_os_SharedMemory),
REG_JNI(register_android_os_incremental_IncrementalManager),
+ REG_JNI(register_com_android_internal_content_om_OverlayConfig),
REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
REG_JNI(register_com_android_internal_os_Zygote),
REG_JNI(register_com_android_internal_os_ZygoteInit),
@@ -1558,6 +1562,8 @@
REG_JNI(register_android_security_Scrypt),
REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
REG_JNI(register_com_android_internal_os_FuseAppLoop),
+ REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader),
+ REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader),
};
/*
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 30e914d..130322a 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -19,11 +19,10 @@
#include <utils/Color.h>
#ifdef __ANDROID__ // Layoutlib does not support graphic buffer, parcel or render thread
-#include <binder/Parcel.h>
-#include <renderthread/RenderProxy.h>
#include <android_runtime/android_graphics_GraphicBuffer.h>
-#include <android_runtime/android_hardware_HardwareBuffer.h>
-#include <private/android/AHardwareBufferHelpers.h>
+#include <binder/Parcel.h>
+#include <dlfcn.h>
+#include <renderthread/RenderProxy.h>
#endif
#include "core_jni_helpers.h"
@@ -1027,11 +1026,18 @@
return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false));
}
+#ifdef __ANDROID__ // Layoutlib does not support graphic buffer
+typedef AHardwareBuffer* (*AHB_from_HB)(JNIEnv*, jobject);
+AHB_from_HB AHardwareBuffer_fromHardwareBuffer;
+
+typedef jobject (*AHB_to_HB)(JNIEnv*, AHardwareBuffer*);
+AHB_to_HB AHardwareBuffer_toHardwareBuffer;
+#endif
+
static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer,
jlong colorSpacePtr) {
#ifdef __ANDROID__ // Layoutlib does not support graphic buffer
- AHardwareBuffer* buffer = android_hardware_HardwareBuffer_getNativeHardwareBuffer(env,
- hardwareBuffer);
+ AHardwareBuffer* buffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBuffer);
sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer,
GraphicsJNI::getNativeColorSpace(colorSpacePtr));
if (!bitmap.get()) {
@@ -1057,6 +1063,19 @@
#endif
}
+static jobject Bitmap_getHardwareBuffer(JNIEnv* env, jobject, jlong bitmapPtr) {
+#ifdef __ANDROID__ // Layoutlib does not support graphic buffer
+ LocalScopedBitmap bitmapHandle(bitmapPtr);
+ LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(),
+ "Hardware config is only supported config in Bitmap_getHardwareBuffer");
+
+ Bitmap& bitmap = bitmapHandle->bitmap();
+ return AHardwareBuffer_toHardwareBuffer(env, bitmap.hardwareBuffer());
+#else
+ return NULL;
+#endif
+}
+
static jboolean Bitmap_isImmutable(CRITICAL_JNI_PARAMS_COMMA jlong bitmapHandle) {
LocalScopedBitmap bitmapHolder(bitmapHandle);
if (!bitmapHolder.valid()) return JNI_FALSE;
@@ -1123,6 +1142,8 @@
(void*) Bitmap_wrapHardwareBufferBitmap },
{ "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;",
(void*) Bitmap_createGraphicBufferHandle },
+ { "nativeGetHardwareBuffer", "(J)Landroid/hardware/HardwareBuffer;",
+ (void*) Bitmap_getHardwareBuffer },
{ "nativeComputeColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*)Bitmap_computeColorSpace },
{ "nativeSetColorSpace", "(JJ)V", (void*)Bitmap_setColorSpace },
{ "nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB },
@@ -1140,6 +1161,18 @@
gBitmap_nativePtr = GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J");
gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "<init>", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V");
gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V");
+
+#ifdef __ANDROID__ // Layoutlib does not support graphic buffer
+ void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+ AHardwareBuffer_fromHardwareBuffer =
+ (AHB_from_HB)dlsym(handle_, "AHardwareBuffer_fromHardwareBuffer");
+ LOG_ALWAYS_FATAL_IF(AHardwareBuffer_fromHardwareBuffer == nullptr,
+ "Failed to find required symbol AHardwareBuffer_fromHardwareBuffer!");
+
+ AHardwareBuffer_toHardwareBuffer = (AHB_to_HB)dlsym(handle_, "AHardwareBuffer_toHardwareBuffer");
+ LOG_ALWAYS_FATAL_IF(AHardwareBuffer_toHardwareBuffer == nullptr,
+ " Failed to find required symbol AHardwareBuffer_toHardwareBuffer!");
+#endif
return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods,
NELEM(gBitmapMethods));
}
diff --git a/core/jni/com_android_internal_content_om_OverlayConfig.cpp b/core/jni/com_android_internal_content_om_OverlayConfig.cpp
new file mode 100644
index 0000000..6aa7c10
--- /dev/null
+++ b/core/jni/com_android_internal_content_om_OverlayConfig.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include "core_jni_helpers.h"
+
+#include "android-base/logging.h"
+#include "androidfw/PosixUtils.h"
+
+using ::android::util::ExecuteBinary;
+
+static jclass g_stringClass = nullptr;
+
+static jobjectArray createIdmap(JNIEnv* env, jclass /*clazz*/, jstring targetPath,
+ jobjectArray overlayPath, jobjectArray policies,
+ jboolean enforceOverlayable) {
+ if (access("/system/bin/idmap2", X_OK) == -1) {
+ PLOG(WARNING) << "unable to execute idmap2";
+ return nullptr;
+ }
+
+ const char* targetApkPath = env->GetStringUTFChars(targetPath, NULL /* isCopy */);
+ std::vector<std::string> argv{"/system/bin/idmap2",
+ "create-multiple",
+ "--target-apk-path", targetApkPath,
+ };
+ env->ReleaseStringUTFChars(targetPath, targetApkPath);
+
+ // Add the overlays for which to generate idmap files to the idmap arguments.
+ for (size_t i = 0, count = env->GetArrayLength(overlayPath); i < count; ++i) {
+ jstring element = (jstring) env->GetObjectArrayElement(overlayPath, i);
+ const char* overlayApkPath = env->GetStringUTFChars(element, NULL /* isCopy */);
+ argv.emplace_back("--overlay-apk-path");
+ argv.emplace_back(overlayApkPath);
+ env->ReleaseStringUTFChars(element, overlayApkPath);
+ }
+
+ // Add the policies the overlays fulfill to the idmap arguments.
+ for (size_t i = 0, count = env->GetArrayLength(policies); i < count; ++i) {
+ jstring element = (jstring)env->GetObjectArrayElement(policies, i);
+ const char* policy = env->GetStringUTFChars(element, NULL /* isCopy */);
+ argv.emplace_back("--policy");
+ argv.emplace_back(policy);
+ env->ReleaseStringUTFChars(element, policy);
+ }
+
+ if (!enforceOverlayable) {
+ argv.emplace_back("--ignore-overlayable");
+ }
+
+ const auto result = ExecuteBinary(argv);
+ if (!result) {
+ LOG(ERROR) << "failed to execute idmap2";
+ return nullptr;
+ }
+
+ if (result->status != 0) {
+ LOG(ERROR) << "idmap2: " << result->stderr;
+ return nullptr;
+ }
+
+ // Return the paths of the idmaps created or updated during the idmap invocation.
+ std::vector<std::string> idmap_paths;
+ std::istringstream input(result->stdout);
+ std::string path;
+ while (std::getline(input, path)) {
+ idmap_paths.push_back(path);
+ }
+
+ jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr);
+ if (array == nullptr) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < idmap_paths.size(); i++) {
+ const std::string path = idmap_paths[i];
+ jstring java_string = env->NewStringUTF(path.c_str());
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ env->SetObjectArrayElement(array, i, java_string);
+ env->DeleteLocalRef(java_string);
+ }
+
+ return array;
+}
+
+static const JNINativeMethod g_methods[] = {
+ { "createIdmap",
+ "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Z)[Ljava/lang/String;",
+ (void *)createIdmap },
+};
+
+static const char* const kOverlayConfigPathName = "com/android/internal/content/om/OverlayConfig";
+
+namespace android {
+
+int register_com_android_internal_content_om_OverlayConfig(JNIEnv* env) {
+ jclass stringClass = FindClassOrDie(env, "java/lang/String");
+ g_stringClass = MakeGlobalRefOrDie(env, stringClass);
+
+ return RegisterMethodsOrDie(env, kOverlayConfigPathName, g_methods, NELEM(g_methods));
+}
+
+} // namespace android
diff --git a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
new file mode 100644
index 0000000..7c68de5
--- /dev/null
+++ b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+
+#include "core_jni_helpers.h"
+
+#include <sys/sysinfo.h>
+
+#include <android-base/stringprintf.h>
+#include <cputimeinstate.h>
+
+namespace android {
+
+static constexpr uint64_t NSEC_PER_MSEC = 1000000;
+
+static struct {
+ jclass clazz;
+ jmethodID put;
+ jmethodID get;
+} gSparseArrayClassInfo;
+
+static jfieldID gmData;
+
+static jlongArray getUidArray(JNIEnv *env, jobject sparseAr, uint32_t uid, jsize sz) {
+ jlongArray ar = (jlongArray)env->CallObjectMethod(sparseAr, gSparseArrayClassInfo.get, uid);
+ if (!ar) {
+ ar = env->NewLongArray(sz);
+ if (ar == NULL) return ar;
+ env->CallVoidMethod(sparseAr, gSparseArrayClassInfo.put, uid, ar);
+ }
+ return ar;
+}
+
+static void copy2DVecToArray(JNIEnv *env, jlongArray ar, std::vector<std::vector<uint64_t>> &vec) {
+ jsize start = 0;
+ for (auto &subVec : vec) {
+ for (uint32_t i = 0; i < subVec.size(); ++i) subVec[i] /= NSEC_PER_MSEC;
+ env->SetLongArrayRegion(ar, start, subVec.size(),
+ reinterpret_cast<const jlong *>(subVec.data()));
+ start += subVec.size();
+ }
+}
+
+static jboolean KernelCpuUidFreqTimeBpfMapReader_removeUidRange(JNIEnv *env, jclass, jint startUid,
+ jint endUid) {
+ for (uint32_t uid = startUid; uid <= endUid; ++uid) {
+ if (!android::bpf::clearUidTimes(uid)) return false;
+ }
+ return true;
+}
+
+static jboolean KernelCpuUidFreqTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
+ static uint64_t lastUpdate = 0;
+ uint64_t newLastUpdate = lastUpdate;
+ auto sparseAr = env->GetObjectField(thiz, gmData);
+ if (sparseAr == NULL) return false;
+ auto data = android::bpf::getUidsUpdatedCpuFreqTimes(&newLastUpdate);
+ if (!data.has_value()) return false;
+
+ jsize s = 0;
+ for (auto &[uid, times] : *data) {
+ if (s == 0) {
+ for (const auto &subVec : times) s += subVec.size();
+ }
+ jlongArray ar = getUidArray(env, sparseAr, uid, s);
+ if (ar == NULL) return false;
+ copy2DVecToArray(env, ar, times);
+ }
+ lastUpdate = newLastUpdate;
+ return true;
+}
+
+static jlongArray KernelCpuUidFreqTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
+ auto freqs = android::bpf::getCpuFreqs();
+ if (!freqs) return NULL;
+
+ std::vector<uint64_t> allFreqs;
+ for (const auto &vec : *freqs) std::copy(vec.begin(), vec.end(), std::back_inserter(allFreqs));
+
+ auto ar = env->NewLongArray(allFreqs.size());
+ if (ar != NULL) {
+ env->SetLongArrayRegion(ar, 0, allFreqs.size(),
+ reinterpret_cast<const jlong *>(allFreqs.data()));
+ }
+ return ar;
+}
+
+static const JNINativeMethod gFreqTimeMethods[] = {
+ {"removeUidRange", "(II)Z", (void *)KernelCpuUidFreqTimeBpfMapReader_removeUidRange},
+ {"readBpfData", "()Z", (void *)KernelCpuUidFreqTimeBpfMapReader_readBpfData},
+ {"getDataDimensions", "()[J", (void *)KernelCpuUidFreqTimeBpfMapReader_getDataDimensions},
+};
+
+static jboolean KernelCpuUidActiveTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
+ static uint64_t lastUpdate = 0;
+ uint64_t newLastUpdate = lastUpdate;
+ auto sparseAr = env->GetObjectField(thiz, gmData);
+ if (sparseAr == NULL) return false;
+ auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
+ if (!data.has_value()) return false;
+
+ for (auto &[uid, times] : *data) {
+ // TODO: revise calling code so we can divide by NSEC_PER_MSEC here instead
+ for (auto &time : times.active) time /= NSEC_PER_MSEC;
+ jlongArray ar = getUidArray(env, sparseAr, uid, times.active.size());
+ if (ar == NULL) return false;
+ env->SetLongArrayRegion(ar, 0, times.active.size(),
+ reinterpret_cast<const jlong *>(times.active.data()));
+ }
+ lastUpdate = newLastUpdate;
+ return true;
+}
+
+static jlongArray KernelCpuUidActiveTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
+ jlong nCpus = get_nprocs_conf();
+
+ auto ar = env->NewLongArray(1);
+ if (ar != NULL) env->SetLongArrayRegion(ar, 0, 1, &nCpus);
+ return ar;
+}
+
+static const JNINativeMethod gActiveTimeMethods[] = {
+ {"readBpfData", "()Z", (void *)KernelCpuUidActiveTimeBpfMapReader_readBpfData},
+ {"getDataDimensions", "()[J", (void *)KernelCpuUidActiveTimeBpfMapReader_getDataDimensions},
+};
+
+static jboolean KernelCpuUidClusterTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
+ static uint64_t lastUpdate = 0;
+ uint64_t newLastUpdate = lastUpdate;
+ auto sparseAr = env->GetObjectField(thiz, gmData);
+ if (sparseAr == NULL) return false;
+ auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
+ if (!data.has_value()) return false;
+
+ jsize s = 0;
+ for (auto &[uid, times] : *data) {
+ if (s == 0) {
+ for (const auto &subVec : times.policy) s += subVec.size();
+ }
+ jlongArray ar = getUidArray(env, sparseAr, uid, s);
+ if (ar == NULL) return false;
+ copy2DVecToArray(env, ar, times.policy);
+ }
+ lastUpdate = newLastUpdate;
+ return true;
+}
+
+static jlongArray KernelCpuUidClusterTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
+ auto times = android::bpf::getUidConcurrentTimes(0);
+ if (!times.has_value()) return NULL;
+
+ std::vector<jlong> clusterCores;
+ for (const auto &vec : times->policy) clusterCores.push_back(vec.size());
+ auto ar = env->NewLongArray(clusterCores.size());
+ if (ar != NULL) env->SetLongArrayRegion(ar, 0, clusterCores.size(), clusterCores.data());
+ return ar;
+}
+
+static const JNINativeMethod gClusterTimeMethods[] = {
+ {"readBpfData", "()Z", (void *)KernelCpuUidClusterTimeBpfMapReader_readBpfData},
+ {"getDataDimensions", "()[J",
+ (void *)KernelCpuUidClusterTimeBpfMapReader_getDataDimensions},
+};
+
+struct readerMethods {
+ const char *name;
+ const JNINativeMethod *methods;
+ int numMethods;
+};
+
+static const readerMethods gAllMethods[] = {
+ {"KernelCpuUidFreqTimeBpfMapReader", gFreqTimeMethods, NELEM(gFreqTimeMethods)},
+ {"KernelCpuUidActiveTimeBpfMapReader", gActiveTimeMethods, NELEM(gActiveTimeMethods)},
+ {"KernelCpuUidClusterTimeBpfMapReader", gClusterTimeMethods, NELEM(gClusterTimeMethods)},
+};
+
+static jboolean KernelCpuUidBpfMapReader_startTrackingBpfTimes(JNIEnv *, jobject) {
+ return android::bpf::startTrackingUidTimes();
+}
+
+int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env) {
+ gSparseArrayClassInfo.clazz = FindClassOrDie(env, "android/util/SparseArray");
+ gSparseArrayClassInfo.clazz = MakeGlobalRefOrDie(env, gSparseArrayClassInfo.clazz);
+ gSparseArrayClassInfo.put =
+ GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "put", "(ILjava/lang/Object;)V");
+ gSparseArrayClassInfo.get =
+ GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "get", "(I)Ljava/lang/Object;");
+ constexpr auto readerName = "com/android/internal/os/KernelCpuUidBpfMapReader";
+ constexpr JNINativeMethod method = {"startTrackingBpfTimes", "()Z",
+ (void *)KernelCpuUidBpfMapReader_startTrackingBpfTimes};
+
+ int ret = RegisterMethodsOrDie(env, readerName, &method, 1);
+ if (ret < 0) return ret;
+ auto c = FindClassOrDie(env, readerName);
+ gmData = GetFieldIDOrDie(env, c, "mData", "Landroid/util/SparseArray;");
+
+ for (const auto &m : gAllMethods) {
+ auto fullName = android::base::StringPrintf("%s$%s", readerName, m.name);
+ ret = RegisterMethodsOrDie(env, fullName.c_str(), m.methods, m.numMethods);
+ if (ret < 0) break;
+ }
+ return ret;
+}
+
+} // namespace android
diff --git a/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp b/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp
new file mode 100644
index 0000000..c0ecf33
--- /dev/null
+++ b/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#include "core_jni_helpers.h"
+
+#include <cputimeinstate.h>
+
+namespace android {
+
+static constexpr uint64_t NSEC_PER_MSEC = 1000000;
+
+static jlongArray copyVecsToArray(JNIEnv *env, std::vector<std::vector<uint64_t>> &vec) {
+ jsize s = 0;
+ for (const auto &subVec : vec) s += subVec.size();
+ jlongArray ar = env->NewLongArray(s);
+ jsize start = 0;
+ for (auto &subVec : vec) {
+ for (uint32_t i = 0; i < subVec.size(); ++i) subVec[i] /= NSEC_PER_MSEC;
+ env->SetLongArrayRegion(ar, start, subVec.size(),
+ reinterpret_cast<const jlong*>(subVec.data()));
+ start += subVec.size();
+ }
+ return ar;
+}
+
+static jlongArray getUidCpuFreqTimeMs(JNIEnv *env, jclass, jint uid) {
+ auto out = android::bpf::getUidCpuFreqTimes(uid);
+ if (!out) return env->NewLongArray(0);
+ return copyVecsToArray(env, out.value());
+}
+
+static const JNINativeMethod g_single_methods[] = {
+ {"readBpfData", "(I)[J", (void *)getUidCpuFreqTimeMs},
+};
+
+int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env) {
+ return RegisterMethodsOrDie(env, "com/android/internal/os/KernelSingleUidTimeReader$Injector",
+ g_single_methods, NELEM(g_single_methods));
+}
+
+}
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index c039570..6850d01 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -366,6 +366,15 @@
// Whether battery level is low or not.
optional bool is_battery_level_low = 8;
+ // Denotes which threshold should be used for automatic Battery Saver triggering.
+ enum AutomaticTriggerEnum {
+ TRIGGER_PERCENTAGE = 0;
+ TRIGGER_DYNAMIC = 1;
+ }
+ // The value of Global.AUTOMATIC_POWER_SAVE_MODE. This is a cached value, so it could
+ // be slightly different from what's in GlobalSettingsProto.DynamicPowerSavings.
+ optional AutomaticTriggerEnum setting_automatic_trigger = 19;
+
// The value of Global.LOW_POWER_MODE. This is a cached value, so it could
// be slightly different from what's in GlobalSettingsProto.LowPowerMode.
optional bool setting_battery_saver_enabled = 9;
@@ -390,5 +399,18 @@
// using elapsed realtime as the timebase.
optional int64 last_adaptive_battery_saver_changed_externally_elapsed = 17;
- // Next tag: 19
+ // The default disable threshold for Dynamic Power Savings enabled battery saver.
+ optional int32 default_dynamic_disable_threshold = 20;
+
+ // When to disable battery saver again if it was enabled due to an external suggestion.
+ // Corresponds to Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD. This is a cached value,
+ // so it could be slightly different from what's in GlobalSettingsProto.DynamicPowerSavings.
+ optional int32 dynamic_disable_threshold = 21;
+
+ // Whether we've received a suggestion that battery saver should be on from an external app.
+ // Corresponds to Global.DYNAMIC_POWER_SAVINGS_ENABLED. This is a cached value, so it could
+ // be slightly different from what's in GlobalSettingsProto.DynamicPowerSavings.
+ optional bool dynamic_battery_saver_enabled = 22;
+
+ // Next tag: 23
}
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 7098c95..9aecfd9 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -30,8 +30,8 @@
android:background="?attr/selectableItemBackgroundBorderless">
<ImageView android:id="@+id/icon"
- android:layout_width="@dimen/resolver_icon_size"
- android:layout_height="@dimen/resolver_icon_size"
+ android:layout_width="@dimen/chooser_icon_size"
+ android:layout_height="@dimen/chooser_icon_size"
android:scaleType="fitCenter" />
<!-- Size manually tuned to match specs -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b82591f..c15b794 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3534,9 +3534,6 @@
<item>com.android.dialer</item>
</string-array>
- <!-- An array of packages which can listen for notifications on low ram devices. -->
- <string-array translatable="false" name="config_allowedManagedServicesOnLowRamDevices" />
-
<!-- The default value for transition animation scale found in developer settings.
1.0 corresponds to 1x animator scale, 0 means that there will be no transition
animations. Note that this is only a default and will be overridden by a
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index c44a0be..48049b4 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -768,6 +768,7 @@
<dimen name="chooser_header_scroll_elevation">4dp</dimen>
<dimen name="chooser_max_collapsed_height">288dp</dimen>
<dimen name="chooser_direct_share_label_placeholder_max_width">72dp</dimen>
+ <dimen name="chooser_icon_size">42dp</dimen>
<dimen name="resolver_icon_size">32dp</dimen>
<dimen name="resolver_button_bar_spacing">8dp</dimen>
<dimen name="resolver_badge_size">18dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 02d90a7..b8dd418 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3167,8 +3167,6 @@
<java-symbol type="array" name="config_nonBlockableNotificationPackages" />
<java-symbol type="array" name="config_priorityOnlyDndExemptPackages" />
- <java-symbol type="array" name="config_allowedManagedServicesOnLowRamDevices" />
-
<!-- Screen-size-dependent modes for picker dialogs. -->
<java-symbol type="integer" name="time_picker_mode" />
<java-symbol type="integer" name="date_picker_mode" />
@@ -3765,6 +3763,7 @@
<java-symbol type="dimen" name="resolver_small_margin"/>
<java-symbol type="dimen" name="resolver_edge_margin"/>
<java-symbol type="dimen" name="resolver_elevation"/>
+ <java-symbol type="dimen" name="chooser_icon_size"/>
<!-- For DropBox -->
<java-symbol type="integer" name="config_dropboxLowPriorityBroadcastRateLimitPeriod" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 18778b9..0785e42 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -114,6 +114,7 @@
":FrameworksCoreTests_keyset_splata_api",
":FrameworksCoreTests_keyset_splat_api",
":FrameworksCoreTests_locales",
+ ":FrameworksCoreTests_overlay_config",
":FrameworksCoreTests_version_1",
":FrameworksCoreTests_version_1_diff",
":FrameworksCoreTests_version_1_nosys",
diff --git a/core/tests/coretests/apks/overlay_config/Android.bp b/core/tests/coretests/apks/overlay_config/Android.bp
new file mode 100644
index 0000000..9573557
--- /dev/null
+++ b/core/tests/coretests/apks/overlay_config/Android.bp
@@ -0,0 +1,4 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_overlay_config",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+}
diff --git a/core/tests/coretests/apks/overlay_config/AndroidManifest.xml b/core/tests/coretests/apks/overlay_config/AndroidManifest.xml
new file mode 100644
index 0000000..b15338e
--- /dev/null
+++ b/core/tests/coretests/apks/overlay_config/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.overlay_config">
+
+ <application android:hasCode="false" />
+
+ <uses-sdk android:targetSdkVersion="21"/>
+
+ <overlay android:targetPackage="android"
+ android:targetName="TestResources" />
+</manifest>
diff --git a/core/tests/coretests/src/com/android/internal/content/OverlayConfigIterationRule.java b/core/tests/coretests/src/com/android/internal/content/OverlayConfigIterationRule.java
new file mode 100644
index 0000000..23655a0
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/content/OverlayConfigIterationRule.java
@@ -0,0 +1,162 @@
+/*
+ * 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.internal.content;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.parsing.AndroidPackage;
+import android.os.Build;
+import android.util.ArrayMap;
+
+import com.android.internal.content.om.OverlayConfig.AndroidPackageProvider;
+import com.android.internal.content.om.OverlayScanner;
+import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
+
+import org.junit.Assert;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * A {@link TestRule} that runs a test case twice. First, the test case runs with a non-null
+ * {@link OverlayScanner} as if the zygote process is scanning the overlay packages
+ * and parsing configuration files. The test case then runs with a non-null
+ * {@link AndroidPackageProvider} as if the system server is parsing configuration files.
+ *
+ * This simulates what will happen on device. If an exception would be thrown in the zygote, then
+ * the exception should be thrown in the first run of the test case.
+ */
+public class OverlayConfigIterationRule implements TestRule {
+
+ enum Iteration {
+ ZYGOTE,
+ SYSTEM_SERVER,
+ }
+
+ private final ArrayMap<File, ParsedOverlayInfo> mOverlayStubResults = new ArrayMap<>();
+ private Supplier<OverlayScanner> mOverlayScanner;
+ private AndroidPackageProvider mAndroidPackageProvider;
+ private Iteration mIteration;
+
+ /**
+ * Mocks the parsing of the file to make it appear to the scanner that the file is a valid
+ * overlay APK.
+ **/
+ void addOverlay(File path, String packageName, String targetPackage, int targetSdkVersion,
+ boolean isStatic, int priority) {
+ try {
+ final File canonicalPath = new File(path.getCanonicalPath());
+ mOverlayStubResults.put(canonicalPath, new ParsedOverlayInfo(
+ packageName, targetPackage, targetSdkVersion, isStatic, priority,
+ canonicalPath));
+ } catch (IOException e) {
+ Assert.fail("Failed to add overlay " + e);
+ }
+ }
+
+ void addOverlay(File path, String packageName) {
+ addOverlay(path, packageName, "target");
+ }
+
+ void addOverlay(File path, String packageName, String targetPackage) {
+ addOverlay(path, packageName, targetPackage, Build.VERSION_CODES.CUR_DEVELOPMENT);
+ }
+
+ void addOverlay(File path, String packageName, String targetPackage, int targetSdkVersion) {
+ addOverlay(path, packageName, targetPackage, targetSdkVersion, false, 0);
+ }
+
+ /** Retrieves the {@link OverlayScanner} for the current run of the test. */
+ Supplier<OverlayScanner> getScannerFactory() {
+ return mOverlayScanner;
+ }
+
+ /** Retrieves the {@link AndroidPackageProvider} for the current run of the test. */
+ AndroidPackageProvider getPackageProvider() {
+ return mAndroidPackageProvider;
+ }
+
+ /** Retrieves the current iteration of the test. */
+ Iteration getIteration() {
+ return mIteration;
+ }
+
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ // Run the test once as if the zygote process is scanning the overlay packages
+ // and parsing configuration files.
+ mOverlayScanner = () -> {
+ OverlayScanner scanner = Mockito.spy(new OverlayScanner());
+ for (Map.Entry<File, ParsedOverlayInfo> overlay :
+ mOverlayStubResults.entrySet()) {
+ doReturn(overlay.getValue()).when(scanner)
+ .parseOverlayManifest(overlay.getKey());
+ }
+ return scanner;
+ };
+ mAndroidPackageProvider = null;
+ mIteration = Iteration.ZYGOTE;
+ base.evaluate();
+
+ // Run the test once more (if the first test did not throw an exception) as if
+ // the system server is parsing the configuration files and using PackageManager to
+ // retrieving information of overlays.
+ mOverlayScanner = null;
+ mAndroidPackageProvider = Mockito.mock(AndroidPackageProvider.class);
+ mIteration = Iteration.SYSTEM_SERVER;
+ doAnswer((InvocationOnMock invocation) -> {
+ final Object[] args = invocation.getArguments();
+ final Consumer<AndroidPackage> f = (Consumer<AndroidPackage>) args[0];
+ for (Map.Entry<File, ParsedOverlayInfo> overlay :
+ mOverlayStubResults.entrySet()) {
+ final AndroidPackage a = Mockito.mock(AndroidPackage.class);
+ final ParsedOverlayInfo info = overlay.getValue();
+ when(a.getPackageName()).thenReturn(info.packageName);
+ when(a.getOverlayTarget()).thenReturn(info.targetPackageName);
+ when(a.getTargetSdkVersion()).thenReturn(info.targetSdkVersion);
+ when(a.isOverlayIsStatic()).thenReturn(info.isStatic);
+ when(a.getOverlayPriority()).thenReturn(info.priority);
+ when(a.getBaseCodePath()).thenReturn(info.path.getPath());
+ when(a.isSystem()).thenReturn(
+ !info.path.getPath().contains("data/overlay"));
+ f.accept(a);
+ }
+ return null;
+ }).when(mAndroidPackageProvider).forEachPackage(any());
+
+ base.evaluate();
+ }
+ };
+ }
+}
+
+
diff --git a/core/tests/coretests/src/com/android/internal/content/OverlayConfigTest.java b/core/tests/coretests/src/com/android/internal/content/OverlayConfigTest.java
new file mode 100644
index 0000000..dee118f
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/content/OverlayConfigTest.java
@@ -0,0 +1,603 @@
+/*
+ * 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.internal.content;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.FileUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.content.om.OverlayConfig.IdmapInvocation;
+import com.android.internal.content.om.OverlayScanner;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+public class OverlayConfigTest {
+ private static final String TEST_APK_PACKAGE_NAME =
+ "com.android.frameworks.coretests.overlay_config";
+
+ private ExpectedException mExpectedException = ExpectedException.none();
+ private OverlayConfigIterationRule mScannerRule = new OverlayConfigIterationRule();
+ private TemporaryFolder mTestFolder = new TemporaryFolder();
+
+ @Rule
+ public RuleChain chain = RuleChain.outerRule(mExpectedException)
+ .around(mTestFolder).around(mScannerRule);
+
+ private OverlayConfig createConfigImpl() throws IOException {
+ return new OverlayConfig(mTestFolder.getRoot().getCanonicalFile(),
+ mScannerRule.getScannerFactory(), mScannerRule.getPackageProvider());
+ }
+
+ private File createFile(String fileName) throws IOException {
+ return createFile(fileName, "");
+ }
+
+ private File createFile(String fileName, String content) throws IOException {
+ final File f = new File(String.format("%s/%s", mTestFolder.getRoot(), fileName));
+ if (!f.getParentFile().equals(mTestFolder.getRoot())) {
+ f.getParentFile().mkdirs();
+ }
+ FileUtils.stringToFile(f.getPath(), content);
+ return f;
+ }
+
+ private static void assertConfig(OverlayConfig overlayConfig, String packageName,
+ boolean mutable, boolean enabled, int configIndex) {
+ final OverlayConfig.Configuration config = overlayConfig.getConfiguration(packageName);
+ assertNotNull(config);
+ assertEquals(mutable, config.parsedConfig.mutable);
+ assertEquals(enabled, config.parsedConfig.enabled);
+ assertEquals(configIndex, config.configIndex);
+ }
+
+ @Test
+ public void testImmutableAfterNonImmutableFails() throws IOException {
+ mExpectedException.expect(IllegalStateException.class);
+ mExpectedException.expectMessage("immutable overlays must precede mutable overlays");
+
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" enabled=\"true\" />"
+ + " <overlay package=\"two\" mutable=\"false\" enabled=\"true\" />"
+ + "</config>");
+
+ mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one");
+ mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two");
+ createConfigImpl();
+ }
+
+ @Test
+ public void testConfigureAbsentPackageFails() throws IOException {
+ mExpectedException.expect(IllegalStateException.class);
+ mExpectedException.expectMessage("not present in partition");
+
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" enabled=\"true\" />"
+ + "</config>");
+
+ createConfigImpl();
+ }
+
+ @Test
+ public void testConfigurePackageTwiceFails() throws IOException {
+ mExpectedException.expect(IllegalStateException.class);
+ mExpectedException.expectMessage("configured multiple times in a single partition");
+
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" enabled=\"true\" />"
+ + " <overlay package=\"one\" mutable=\"false\" />"
+ + "</config>");
+
+ mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one");
+ createConfigImpl();
+ }
+
+ @Test
+ public void testConfigureOverlayAcrossPartitionsFails() throws IOException {
+ mExpectedException.expect(IllegalStateException.class);
+ mExpectedException.expectMessage("not present in partition");
+
+ createFile("/vendor/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" enabled=\"true\" />"
+ + "</config>");
+
+ mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one");
+ createConfigImpl();
+ }
+
+ @Test
+ public void testConfigureOverlayOutsideOverlayDirFails() throws IOException {
+ mExpectedException.expect(IllegalStateException.class);
+ mExpectedException.expectMessage("not present in partition");
+
+ createFile("/vendor/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" enabled=\"true\" />"
+ + "</config>");
+
+ mScannerRule.addOverlay(createFile("/product/app/one.apk"), "one");
+ createConfigImpl();
+ }
+
+ @Test
+ public void testMergeOAbsolutePathFails() throws IOException {
+ mExpectedException.expect(IllegalStateException.class);
+ mExpectedException.expectMessage("must be relative to the directory");
+
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <merge path=\"/product/overlay/config/auto-generated-config.xml\" />"
+ + "</config>");
+
+ createConfigImpl();
+ }
+
+ @Test
+ public void testMergeOutsideDirFails() throws IOException {
+ mExpectedException.expect(IllegalStateException.class);
+ mExpectedException.expectMessage("outside of configuration directory");
+
+ createFile("/product/overlay/auto-generated-config.xml");
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <merge path=\"../auto-generated-config.xml\" />"
+ + "</config>");
+
+ createConfigImpl();
+ }
+
+ @Test
+ public void testMergeOutsidePartitionFails() throws IOException {
+ mExpectedException.expect(IllegalStateException.class);
+ mExpectedException.expectMessage("outside of configuration directory");
+
+ createFile("/vendor/overlay/config/config2.xml");
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <merge path=\"../../../vendor/overlay/config/config2.xml\" />"
+ + "</config>");
+
+ createConfigImpl();
+ }
+
+ @Test
+ public void testMergeCircularFails() throws IOException {
+ mExpectedException.expect(IllegalStateException.class);
+ mExpectedException.expectMessage("Maximum <merge> depth exceeded");
+
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <merge path=\"config2.xml\" />"
+ + "</config>");
+ createFile("/product/overlay/config/config2.xml",
+ "<config>"
+ + " <merge path=\"config.xml\" />"
+ + "</config>");
+
+ createConfigImpl();
+ }
+
+ @Test
+ public void testMergeMissingFileFails() throws IOException {
+ mExpectedException.expect(IllegalStateException.class);
+ mExpectedException.expectMessage("does not exist");
+
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <merge path=\"config2.xml\" />"
+ + "</config>");
+ createConfigImpl();
+ }
+
+ @Test
+ public void testProductOverridesVendor() throws IOException {
+ createFile("/vendor/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" enabled=\"false\" />"
+ + "</config>");
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" enabled=\"true\" />"
+ + "</config>");
+
+ mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one");
+ mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one");
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", true, true, 1);
+ }
+
+ @Test
+ public void testPartitionPrecedence() throws IOException {
+ createFile("/vendor/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" enabled=\"true\" />"
+ + "</config>");
+ createFile("/odm/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"two\" enabled=\"true\" />"
+ + "</config>");
+ createFile("/oem/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"three\" enabled=\"true\" />"
+ + "</config>");
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"four\" enabled=\"true\" />"
+ + "</config>");
+ createFile("/system_ext/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"five\" enabled=\"true\" />"
+ + "</config>");
+
+ mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one");
+ mScannerRule.addOverlay(createFile("/odm/overlay/two.apk"), "two");
+ mScannerRule.addOverlay(createFile("/oem/overlay/three.apk"), "three");
+ mScannerRule.addOverlay(createFile("/product/overlay/four.apk"), "four");
+ mScannerRule.addOverlay(createFile("/system_ext/overlay/five.apk"), "five");
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", true, true, 0);
+ assertConfig(overlayConfig, "two", true, true, 1);
+ assertConfig(overlayConfig, "three", true, true, 2);
+ assertConfig(overlayConfig, "four", true, true, 3);
+ assertConfig(overlayConfig, "five", true, true, 4);
+ }
+
+ @Test
+ public void testImmutable() throws IOException {
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" mutable=\"false\" />"
+ + " <overlay package=\"two\" />"
+ + " <overlay package=\"three\" mutable=\"true\" />"
+ + "</config>");
+
+
+ mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one");
+ mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two");
+ mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three");
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", false, false, 0);
+ assertConfig(overlayConfig, "two", true, false, 1);
+ assertConfig(overlayConfig, "three", true, false, 2);
+ }
+
+ @Test
+ public void testEnabled() throws IOException {
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" />"
+ + " <overlay package=\"two\" enabled=\"true\" />"
+ + " <overlay package=\"three\" enabled=\"false\" />"
+ + "</config>");
+
+
+ mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one");
+ mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two");
+ mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three");
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", true, false, 0);
+ assertConfig(overlayConfig, "two", true, true, 1);
+ assertConfig(overlayConfig, "three", true, false, 2);
+ }
+
+ @Test
+ public void testMerge() throws IOException {
+ createFile("/product/overlay/config/auto-generated-config.xml",
+ "<config>"
+ + " <overlay package=\"two\" mutable=\"false\" enabled=\"true\" />"
+ + " <overlay package=\"three\" mutable=\"false\" enabled=\"true\" />"
+ + "</config>");
+
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" mutable=\"false\" enabled=\"true\" />"
+ + " <merge path=\"auto-generated-config.xml\" />"
+ + " <overlay package=\"four\" enabled=\"true\" />"
+ + "</config>");
+
+ mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one");
+ mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two");
+ mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three");
+ mScannerRule.addOverlay(createFile("/product/overlay/four.apk"), "four");
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ OverlayConfig.Configuration o1 = overlayConfig.getConfiguration("one");
+ assertNotNull(o1);
+ assertFalse(o1.parsedConfig.mutable);
+ assertTrue(o1.parsedConfig.enabled);
+ assertEquals(0, o1.configIndex);
+
+ OverlayConfig.Configuration o2 = overlayConfig.getConfiguration("two");
+ assertNotNull(o2);
+ assertFalse(o2.parsedConfig.mutable);
+ assertTrue(o2.parsedConfig.enabled);
+ assertEquals(1, o2.configIndex);
+
+ OverlayConfig.Configuration o3 = overlayConfig.getConfiguration("three");
+ assertNotNull(o3);
+ assertFalse(o3.parsedConfig.mutable);
+ assertTrue(o3.parsedConfig.enabled);
+ assertEquals(2, o3.configIndex);
+
+ OverlayConfig.Configuration o4 = overlayConfig.getConfiguration("four");
+ assertNotNull(o4);
+ assertTrue(o4.parsedConfig.mutable);
+ assertTrue(o4.parsedConfig.enabled);
+ assertEquals(3, o4.configIndex);
+ }
+
+ @Test
+ public void testIdmapInvocationsFrameworkImmutable() throws IOException {
+ createFile("/vendor/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" mutable=\"false\" enabled=\"true\" />"
+ + " <overlay package=\"two\" mutable=\"false\" enabled=\"true\" />"
+ + " <overlay package=\"three\" enabled=\"true\" />"
+ + "</config>");
+
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"four\" mutable=\"false\" enabled=\"true\" />"
+ + " <overlay package=\"five\" mutable=\"false\" enabled=\"true\" />"
+ + " <overlay package=\"six\" mutable=\"false\" enabled=\"false\" />"
+ + "</config>");
+
+ mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android");
+ mScannerRule.addOverlay(createFile("/vendor/overlay/two.apk"), "two", "android");
+ mScannerRule.addOverlay(createFile("/vendor/overlay/three.apk"), "three", "android");
+ mScannerRule.addOverlay(createFile("/product/overlay/four.apk"), "four", "android");
+ mScannerRule.addOverlay(createFile("/product/overlay/five.apk"), "five");
+ mScannerRule.addOverlay(createFile("/product/overlay/six.apk"), "six", "android");
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ if (mScannerRule.getIteration() == OverlayConfigIterationRule.Iteration.ZYGOTE) {
+ final ArrayList<IdmapInvocation> idmapInvocations =
+ overlayConfig.getImmutableFrameworkOverlayIdmapInvocations();
+ assertEquals(2, idmapInvocations.size());
+
+ final IdmapInvocation i0 = idmapInvocations.get(0);
+ assertTrue(i0.enforceOverlayable);
+ assertEquals("vendor", i0.policy);
+ assertEquals(2, i0.overlayPaths.size());
+ assertTrue(i0.overlayPaths.get(0).endsWith("/vendor/overlay/one.apk"));
+ assertTrue(i0.overlayPaths.get(1).endsWith("/vendor/overlay/two.apk"));
+
+ final IdmapInvocation i1 = idmapInvocations.get(1);
+ assertTrue(i1.enforceOverlayable);
+ assertEquals("product", i1.policy);
+ assertEquals(1, i1.overlayPaths.size());
+ assertTrue(i1.overlayPaths.get(0).endsWith("/product/overlay/four.apk"));
+ }
+ }
+
+ @Test
+ public void testIdmapInvocationsDifferentTargetSdk() throws IOException {
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" mutable=\"false\" enabled=\"true\" />"
+ + " <overlay package=\"two\" mutable=\"false\" enabled=\"true\" />"
+ + " <overlay package=\"three\" mutable=\"false\" enabled=\"true\" />"
+ + " <overlay package=\"four\" mutable=\"false\" enabled=\"true\" />"
+ + "</config>");
+
+ mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one", "android");
+ mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android");
+ mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 28);
+ mScannerRule.addOverlay(createFile("/product/overlay/four.apk"), "four", "android");
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+
+ if (mScannerRule.getIteration() == OverlayConfigIterationRule.Iteration.ZYGOTE) {
+ final ArrayList<IdmapInvocation> idmapInvocations =
+ overlayConfig.getImmutableFrameworkOverlayIdmapInvocations();
+ assertEquals(3, idmapInvocations.size());
+
+ final IdmapInvocation i0 = idmapInvocations.get(0);
+ assertTrue(i0.enforceOverlayable);
+ assertEquals(2, i0.overlayPaths.size());
+ assertTrue(i0.overlayPaths.get(0).endsWith("/product/overlay/one.apk"));
+ assertTrue(i0.overlayPaths.get(1).endsWith("/product/overlay/two.apk"));
+
+ final IdmapInvocation i1 = idmapInvocations.get(1);
+ assertFalse(i1.enforceOverlayable);
+ assertEquals(1, i1.overlayPaths.size());
+ assertTrue(i1.overlayPaths.get(0).endsWith("/product/overlay/three.apk"));
+
+ final IdmapInvocation i2 = idmapInvocations.get(2);
+ assertTrue(i2.enforceOverlayable);
+ assertEquals(1, i2.overlayPaths.size());
+ assertTrue(i2.overlayPaths.get(0).endsWith("/product/overlay/four.apk"));
+ }
+ }
+
+ @Test
+ public void testNoConfigIsStatic() throws IOException {
+ mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one", "android", 28, true,
+ 1);
+ mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 28, false,
+ 0);
+ mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 28,
+ true, 0);
+ mScannerRule.addOverlay(createFile("/product/overlay/four.apk"), "four", "android", 28,
+ false, 2);
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", false, true, 1);
+ assertConfig(overlayConfig, "three", false, true, 0);
+
+ }
+
+ @Test
+ public void testVendorStaticPrecedesProductImmutable() throws IOException {
+ createFile("/product/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"two\" mutable=\"false\" enabled=\"true\" />"
+ + "</config>");
+
+ mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
+ 1);
+ mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, true,
+ 0);
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", false, true, 0);
+ assertConfig(overlayConfig, "two", false, true, 1);
+ }
+
+ @Test
+ public void testVendorImmutablePrecededProductStatic() throws IOException {
+ createFile("/vendor/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"one\" mutable=\"false\" enabled=\"true\" />"
+ + "</config>");
+
+ mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
+ 1);
+ mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, true,
+ 0);
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", false, true, 0);
+ assertConfig(overlayConfig, "two", false, true, 1);
+ }
+
+ @Test
+ public void testNoConfigsAllowPartitionReordering() throws IOException {
+ mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
+ 1);
+ mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, true,
+ 0);
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", false, true, 1);
+ assertConfig(overlayConfig, "two", false, true, 0);
+ }
+
+ @Test
+ public void testConfigDisablesPartitionReordering() throws IOException {
+ createFile("/odm/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"two\" enabled=\"true\" />"
+ + "</config>");
+
+ mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
+ 1);
+ mScannerRule.addOverlay(createFile("/odm/overlay/two.apk"), "two");
+ mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 0,
+ true, 0);
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", false, true, 0);
+ assertConfig(overlayConfig, "two", true, true, 1);
+ assertConfig(overlayConfig, "three", false, true, 2);
+ }
+
+ @Test
+ public void testStaticOverlayOutsideOverlayDir() throws IOException {
+ mScannerRule.addOverlay(createFile("/product/app/one.apk"), "one", "android", 0, true, 0);
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ if (mScannerRule.getIteration() == OverlayConfigIterationRule.Iteration.SYSTEM_SERVER) {
+ assertConfig(overlayConfig, "one", false, true, 0);
+ }
+ }
+
+ @Test
+ public void testSortStaticOverlaysDifferentTargets() throws IOException {
+ mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "other", 0, true, 0);
+ mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, true,
+ 0);
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", false, true, 1);
+ assertConfig(overlayConfig, "two", false, true, 0);
+ }
+
+ @Test
+ public void testSortStaticOverlaysSamePriority() throws IOException {
+ mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
+ 0);
+ mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, true,
+ 0);
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", false, true, 1);
+ assertConfig(overlayConfig, "two", false, true, 0);
+ }
+
+ @Test
+ public void testNonSystemOverlayCannotBeStatic() throws IOException {
+ mScannerRule.addOverlay(createFile("/data/overlay/one.apk"), "one", "android", 0, true,
+ 0);
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertTrue(overlayConfig.isMutable("one"));
+ assertFalse(overlayConfig.isEnabled("one"));
+ assertEquals(Integer.MAX_VALUE, overlayConfig.getPriority("one"));
+ }
+
+ @Test
+ public void testGetOverlayInfo() throws IOException {
+ if (mScannerRule.getIteration() != OverlayConfigIterationRule.Iteration.ZYGOTE) {
+ // Run only one iteration of the test.
+ return;
+ }
+
+ final InputStream is = InstrumentationRegistry.getContext().getResources()
+ .openRawResource(R.raw.overlay_config);
+ final File partitionDir = mTestFolder.newFolder("product", "overlay");
+ final File testApk = new File(partitionDir, "test.apk");
+ FileUtils.copy(is, new FileOutputStream(testApk));
+
+ final OverlayScanner scanner = new OverlayScanner();
+ scanner.scanDir(partitionDir);
+
+ final OverlayScanner.ParsedOverlayInfo info = scanner.getParsedInfo(TEST_APK_PACKAGE_NAME);
+ assertNotNull(info);
+ assertEquals(TEST_APK_PACKAGE_NAME, info.packageName);
+ assertEquals("android", info.targetPackageName);
+ assertEquals(testApk.getPath(), info.path.getPath());
+ assertEquals(21, info.targetSdkVersion);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index a6329298..2ad8e18 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -40,6 +40,7 @@
BatteryStatsUserLifecycleTests.class,
KernelCpuProcStringReaderTest.class,
KernelCpuUidActiveTimeReaderTest.class,
+ KernelCpuUidBpfMapReaderTest.class,
KernelCpuUidClusterTimeReaderTest.class,
KernelCpuUidFreqTimeReaderTest.class,
KernelCpuUidUserSysTimeReaderTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
index 1b13a99..2ccd74e 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
@@ -21,11 +21,11 @@
import android.content.Context;
import android.os.FileUtils;
+import android.util.SparseArray;
import android.util.SparseLongArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
@@ -33,11 +33,15 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Random;
/**
@@ -46,14 +50,16 @@
* $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidActiveTimeReaderTest
*/
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
public class KernelCpuUidActiveTimeReaderTest {
private File mTestDir;
private File mTestFile;
private KernelCpuUidActiveTimeReader mReader;
+ private KernelCpuUidTestBpfMapReader mBpfMapReader;
private VerifiableCallback mCallback;
private Random mRand = new Random(12345);
+ protected boolean mUseBpf;
private final int mCpus = 4;
private final String mHeadline = "cpus: 4\n";
private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
@@ -62,12 +68,22 @@
return InstrumentationRegistry.getContext();
}
+ @Parameters(name="useBpf={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { {true}, {false} });
+ }
+
+ public KernelCpuUidActiveTimeReaderTest(boolean useBpf) {
+ mUseBpf = useBpf;
+ }
+
@Before
public void setUp() {
mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
mTestFile = new File(mTestDir, "test.file");
+ mBpfMapReader = new KernelCpuUidTestBpfMapReader();
mReader = new KernelCpuUidActiveTimeReader(
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
mCallback = new VerifiableCallback();
}
@@ -80,7 +96,7 @@
@Test
public void testReadDelta() throws Exception {
final long[][] times = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times));
+ setCpusAndData(times);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], getActiveTime(times[i]));
@@ -90,7 +106,7 @@
// Verify that a second call will only return deltas.
mCallback.clear();
final long[][] newTimes1 = increaseTime(times);
- writeToFile(mHeadline + uidLines(mUids, newTimes1));
+ setCpusAndData(newTimes1);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], getActiveTime(newTimes1[i]) - getActiveTime(times[i]));
@@ -105,7 +121,7 @@
// Verify that calling with a null callback doesn't result in any crashes
mCallback.clear();
final long[][] newTimes2 = increaseTime(newTimes1);
- writeToFile(mHeadline + uidLines(mUids, newTimes2));
+ setCpusAndData(newTimes2);
mReader.readDelta(null);
mCallback.verifyNoMoreInteractions();
@@ -113,19 +129,20 @@
// the previous call had null callback.
mCallback.clear();
final long[][] newTimes3 = increaseTime(newTimes2);
+ setCpusAndData(newTimes3);
writeToFile(mHeadline + uidLines(mUids, newTimes3));
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], getActiveTime(newTimes3[i]) - getActiveTime(newTimes2[i]));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCpusAndData();
}
@Test
public void testReadAbsolute() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCpusAndData(times1);
mReader.readAbsolute(mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], getActiveTime(times1[i]));
@@ -135,19 +152,19 @@
// Verify that a second call should still return absolute values
mCallback.clear();
final long[][] times2 = increaseTime(times1);
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCpusAndData(times2);
mReader.readAbsolute(mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], getActiveTime(times2[i]));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCpusAndData();
}
@Test
public void testReadDeltaDecreasedTime() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCpusAndData(times1);
mReader.readDelta(mCallback);
// Verify that there should not be a callback for a particular UID if its time decreases.
@@ -155,19 +172,19 @@
final long[][] times2 = increaseTime(times1);
System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
times2[0][0] = 100;
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCpusAndData(times2);
mReader.readDelta(mCallback);
for (int i = 1; i < mUids.length; i++) {
mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCpusAndData();
// Verify that the internal state was not modified.
mCallback.clear();
final long[][] times3 = increaseTime(times2);
times3[0] = increaseTime(times1)[0];
- writeToFile(mHeadline + uidLines(mUids, times3));
+ setCpusAndData(times3);
mReader.readDelta(mCallback);
mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
for (int i = 1; i < mUids.length; i++) {
@@ -179,26 +196,26 @@
@Test
public void testReadDeltaNegativeTime() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCpusAndData(times1);
mReader.readDelta(mCallback);
// Verify that there should not be a callback for a particular UID if its time is -ve.
mCallback.clear();
final long[][] times2 = increaseTime(times1);
times2[0][0] *= -1;
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCpusAndData(times2);
mReader.readDelta(mCallback);
for (int i = 1; i < mUids.length; i++) {
mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCpusAndData();
// Verify that the internal state was not modified.
mCallback.clear();
final long[][] times3 = increaseTime(times2);
times3[0] = increaseTime(times1)[0];
- writeToFile(mHeadline + uidLines(mUids, times3));
+ setCpusAndData(times3);
mReader.readDelta(mCallback);
mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
for (int i = 1; i < mUids.length; i++) {
@@ -207,6 +224,28 @@
mCallback.verifyNoMoreInteractions();
}
+ private void setCpusAndData(long[][] times) throws IOException {
+ if (mUseBpf) {
+ mBpfMapReader.setCpus(new long[]{ mCpus });
+ SparseArray<long[]> data = new SparseArray<>();
+ for (int i = 0; i < mUids.length; i++) {
+ data.put(mUids[i], times[i]);
+ }
+ mBpfMapReader.setData(data);
+ } else {
+ writeToFile(mHeadline + uidLines(mUids, times));
+ }
+ }
+
+ private void clearCpusAndData() {
+ if (mUseBpf) {
+ mBpfMapReader.setCpus(null);
+ mBpfMapReader.setData(new SparseArray<>());
+ } else {
+ assertTrue(mTestFile.delete());
+ }
+ }
+
private String uidLines(int[] uids, long[][] times) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < uids.length; i++) {
@@ -261,4 +300,36 @@
assertEquals(0, mData.size());
}
}
+
+ private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
+ private long[] mCpus;
+ private SparseArray<long[]> mNewData = new SparseArray<>();
+
+ public void setData(SparseArray<long[]> data) {
+ mNewData = data;
+ }
+
+ public void setCpus(long[] cpus) {
+ mCpus = cpus;
+ }
+
+ @Override
+ public final boolean startTrackingBpfTimes() {
+ return true;
+ }
+
+ @Override
+ protected final boolean readBpfData() {
+ if (!mUseBpf) {
+ return false;
+ }
+ mData = mNewData;
+ return true;
+ }
+
+ @Override
+ public final long[] getDataDimensions() {
+ return mCpus;
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
new file mode 100644
index 0000000..257b388
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
@@ -0,0 +1,237 @@
+/*
+ * 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.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.util.SparseArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+
+import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidBpfMapReaderTest {
+ private Random mRand = new Random(12345);
+ private KernelCpuUidTestBpfMapReader mReader;
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ mReader = new KernelCpuUidTestBpfMapReader();
+ }
+
+ /**
+ * Tests that reading returns null if readBpfData() fails.
+ */
+ @Test
+ public void testUnsuccessfulRead() {
+ assertEquals(null, mReader.open());
+ }
+
+ /**
+ * Tests that reading will always return null after 5 failures.
+ */
+ @Test
+ public void testReadErrorsLimit() {
+ for (int i = 0; i < 3; i++) {
+ try (BpfMapIterator iter = mReader.open()) {
+ assertNull(iter);
+ }
+ }
+
+ SparseArray<long[]> data = new SparseArray<>();
+ long[] times = {2};
+ data.put(1, new long[]{2});
+ mReader.setData(data);
+ testOpenAndReadData(data);
+
+ mReader.setData(null);
+ for (int i = 0; i < 3; i++) {
+ try (BpfMapIterator iter = mReader.open(true)) {
+ assertNull(iter);
+ }
+ }
+ mReader.setData(data);
+ try (BpfMapIterator iter = mReader.open(true)) {
+ assertNull(iter);
+ }
+ }
+
+ /** Tests getNextUid functionality. */
+ @Test
+ public void testGetNextUid() {
+ final SparseArray<long[]> data = getTestSparseArray(800, 50);
+ mReader.setData(data);
+ testOpenAndReadData(data);
+ }
+
+ @Test
+ public void testConcurrent() throws Exception {
+ final SparseArray<long[]> data = getTestSparseArray(200, 50);
+ final SparseArray<long[]> data1 = getTestSparseArray(180, 70);
+ final List<Throwable> errs = Collections.synchronizedList(new ArrayList<>());
+ mReader.setData(data);
+ // An additional thread for modifying the data.
+ ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(11);
+ final CountDownLatch ready = new CountDownLatch(10);
+ final CountDownLatch start = new CountDownLatch(1);
+ final CountDownLatch modify = new CountDownLatch(1);
+ final CountDownLatch done = new CountDownLatch(10);
+
+ for (int i = 0; i < 5; i++) {
+ threadPool.submit(() -> {
+ ready.countDown();
+ try {
+ start.await();
+ testOpenAndReadData(data);
+ } catch (Throwable e) {
+ errs.add(e);
+ } finally {
+ done.countDown();
+ }
+ });
+ threadPool.submit(() -> {
+ ready.countDown();
+ try {
+ start.await();
+ // Wait for data modification.
+ modify.await();
+ testOpenAndReadData(data1);
+ } catch (Throwable e) {
+ errs.add(e);
+ } finally {
+ done.countDown();
+ }
+ });
+ }
+
+ assertTrue("Prep timed out", ready.await(100, TimeUnit.MILLISECONDS));
+ start.countDown();
+
+ threadPool.schedule(() -> {
+ mReader.setData(data1);
+ modify.countDown();
+ }, 600, TimeUnit.MILLISECONDS);
+
+ assertTrue("Execution timed out", done.await(3, TimeUnit.SECONDS));
+ threadPool.shutdownNow();
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ errs.forEach(e -> e.printStackTrace(pw));
+
+ assertTrue("All Exceptions:\n" + sw.toString(), errs.isEmpty());
+ }
+
+ @Test
+ public void testRemoveUidsInRange() {
+ final SparseArray<long[]> data = getTestSparseArray(200, 50);
+ mReader.setData(data);
+ testOpenAndReadData(data);
+ SparseArray<long[]> changedData = new SparseArray<>();
+ for (int i = 6; i < 200; i++) {
+ changedData.put(i, data.get(i));
+ }
+ mReader.removeUidsInRange(0, 5);
+ testOpenAndReadData(changedData);
+ }
+
+ private void testOpenAndReadData(SparseArray<long[]> expectedData) {
+ try (BpfMapIterator iter = mReader.open()) {
+ long[] actual;
+ if (expectedData.size() > 0) {
+ actual = new long[expectedData.valueAt(0).length + 1];
+ } else {
+ actual = new long[0];
+ }
+ for (int i = 0; i < expectedData.size(); i++) {
+ assertTrue(iter.getNextUid(actual));
+ assertEquals(expectedData.keyAt(i), actual[0]);
+ assertArrayEquals(expectedData.valueAt(i), Arrays.copyOfRange(actual, 1, actual.length));
+ }
+ assertFalse(iter.getNextUid(actual));
+ assertFalse(iter.getNextUid(actual));
+ }
+ }
+
+
+ private SparseArray<long[]> getTestSparseArray(int uids, int numPerUid) {
+ SparseArray<long[]> data = new SparseArray<>();
+ for (int i = 0; i < uids; i++) {
+ data.put(i, mRand.longs(numPerUid, 0, Long.MAX_VALUE).toArray());
+ }
+ return data;
+ }
+
+
+ private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
+ private SparseArray<long[]> mNewData;
+
+ public final void setData(SparseArray<long[]> newData) {
+ mNewData = newData;
+ }
+
+ @Override
+ public final boolean startTrackingBpfTimes() {
+ return true;
+ }
+
+ @Override
+ public final boolean readBpfData() {
+ if (mNewData == null) {
+ return false;
+ }
+ mData = mNewData;
+ return true;
+ }
+
+ @Override
+ public final long[] getDataDimensions() {
+ return null;
+ }
+
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
index 2ea80da..a0dab28 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
@@ -27,7 +27,6 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
@@ -35,11 +34,15 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Random;
/**
@@ -48,28 +51,42 @@
* $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidClusterTimeReaderTest
*/
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
public class KernelCpuUidClusterTimeReaderTest {
private File mTestDir;
private File mTestFile;
private KernelCpuUidClusterTimeReader mReader;
+ private KernelCpuUidTestBpfMapReader mBpfMapReader;
private VerifiableCallback mCallback;
private Random mRand = new Random(12345);
+ protected boolean mUseBpf;
private final int mCpus = 6;
private final String mHeadline = "policy0: 4 policy4: 2\n";
+ private final long[] mCores = {4, 2};
private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
private Context getContext() {
return InstrumentationRegistry.getContext();
}
+ @Parameters(name="useBpf={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { {true}, {false} });
+ }
+
+ public KernelCpuUidClusterTimeReaderTest(boolean useBpf) {
+ mUseBpf = useBpf;
+ }
+
@Before
public void setUp() {
mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
mTestFile = new File(mTestDir, "test.file");
+ mBpfMapReader = new KernelCpuUidTestBpfMapReader();
mReader = new KernelCpuUidClusterTimeReader(
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader,
+ false);
mCallback = new VerifiableCallback();
}
@@ -82,7 +99,7 @@
@Test
public void testReadDelta() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCoresAndData(times1);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], clusterTime(times1[i]));
@@ -92,7 +109,7 @@
// Verify that a second call will only return deltas.
mCallback.clear();
final long[][] times2 = increaseTime(times1);
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCoresAndData(times2);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
@@ -107,7 +124,7 @@
// Verify that calling with a null callback doesn't result in any crashes
mCallback.clear();
final long[][] times3 = increaseTime(times2);
- writeToFile(mHeadline + uidLines(mUids, times3));
+ setCoresAndData(times3);
mReader.readDelta(null);
mCallback.verifyNoMoreInteractions();
@@ -115,19 +132,19 @@
// the previous call had null callback.
mCallback.clear();
final long[][] times4 = increaseTime(times3);
- writeToFile(mHeadline + uidLines(mUids, times4));
+ setCoresAndData(times4);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], subtract(clusterTime(times4[i]), clusterTime(times3[i])));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCoresAndData();
}
@Test
public void testReadAbsolute() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCoresAndData(times1);
mReader.readAbsolute(mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], clusterTime(times1[i]));
@@ -137,19 +154,19 @@
// Verify that a second call should still return absolute values
mCallback.clear();
final long[][] times2 = increaseTime(times1);
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCoresAndData(times2);
mReader.readAbsolute(mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], clusterTime(times2[i]));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCoresAndData();
}
@Test
public void testReadDeltaDecreasedTime() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCoresAndData(times1);
mReader.readDelta(mCallback);
// Verify that there should not be a callback for a particular UID if its time decreases.
@@ -157,19 +174,19 @@
final long[][] times2 = increaseTime(times1);
System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
times2[0][0] = 100;
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCoresAndData(times2);
mReader.readDelta(mCallback);
for (int i = 1; i < mUids.length; i++) {
mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCoresAndData();
// Verify that the internal state was not modified.
mCallback.clear();
final long[][] times3 = increaseTime(times2);
times3[0] = increaseTime(times1)[0];
- writeToFile(mHeadline + uidLines(mUids, times3));
+ setCoresAndData(times3);
mReader.readDelta(mCallback);
mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
for (int i = 1; i < mUids.length; i++) {
@@ -181,26 +198,26 @@
@Test
public void testReadDeltaNegativeTime() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCoresAndData(times1);
mReader.readDelta(mCallback);
// Verify that there should not be a callback for a particular UID if its time decreases.
mCallback.clear();
final long[][] times2 = increaseTime(times1);
times2[0][0] *= -1;
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCoresAndData(times2);
mReader.readDelta(mCallback);
for (int i = 1; i < mUids.length; i++) {
mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCoresAndData();
// Verify that the internal state was not modified.
mCallback.clear();
final long[][] times3 = increaseTime(times2);
times3[0] = increaseTime(times1)[0];
- writeToFile(mHeadline + uidLines(mUids, times3));
+ setCoresAndData(times3);
mReader.readDelta(mCallback);
mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
for (int i = 1; i < mUids.length; i++) {
@@ -209,6 +226,28 @@
mCallback.verifyNoMoreInteractions();
}
+ private void setCoresAndData(long[][] times) throws IOException {
+ if (mUseBpf) {
+ mBpfMapReader.setClusterCores(mCores);
+ SparseArray<long[]> data = new SparseArray<>();
+ for (int i = 0; i < mUids.length; i++) {
+ data.put(mUids[i], times[i]);
+ }
+ mBpfMapReader.setData(data);
+ } else {
+ writeToFile(mHeadline + uidLines(mUids, times));
+ }
+ }
+
+ private void clearCoresAndData() {
+ if (mUseBpf) {
+ mBpfMapReader.setClusterCores(null);
+ mBpfMapReader.setData(new SparseArray<>());
+ } else {
+ assertTrue(mTestFile.delete());
+ }
+ }
+
private long[] clusterTime(long[] times) {
// Assumes 4 + 2 cores
return new long[]{times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4,
@@ -277,4 +316,36 @@
assertEquals(0, mData.size());
}
}
+
+ private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
+ private long[] mClusterCores;
+ private SparseArray<long[]> mNewData = new SparseArray<>();
+
+ public void setData(SparseArray<long[]> data) {
+ mNewData = data;
+ }
+
+ public void setClusterCores(long[] cores) {
+ mClusterCores = cores;
+ }
+
+ @Override
+ public final boolean startTrackingBpfTimes() {
+ return true;
+ }
+
+ @Override
+ protected final boolean readBpfData() {
+ if (!mUseBpf) {
+ return false;
+ }
+ mData = mNewData;
+ return true;
+ }
+
+ @Override
+ public final long[] getDataDimensions() {
+ return mClusterCores;
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
index 0b6fed3..c60a6d6 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
@@ -29,7 +29,6 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
@@ -37,6 +36,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -45,6 +46,7 @@
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Random;
/**
@@ -53,14 +55,16 @@
* $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidFreqTimeReaderTest
*/
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
public class KernelCpuUidFreqTimeReaderTest {
private File mTestDir;
private File mTestFile;
private KernelCpuUidFreqTimeReader mReader;
+ private KernelCpuUidTestBpfMapReader mBpfMapReader;
private VerifiableCallback mCallback;
@Mock
private PowerProfile mPowerProfile;
+ private boolean mUseBpf;
private Random mRand = new Random(12345);
private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
@@ -69,13 +73,23 @@
return InstrumentationRegistry.getContext();
}
+ @Parameters(name="useBpf={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { {true}, {false} });
+ }
+
+ public KernelCpuUidFreqTimeReaderTest(boolean useBpf) {
+ mUseBpf = useBpf;
+ }
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
mTestFile = new File(mTestDir, "test.file");
+ mBpfMapReader = new KernelCpuUidTestBpfMapReader();
mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
mCallback = new VerifiableCallback();
}
@@ -97,17 +111,17 @@
final int[][] numFreqs = {{3, 6}, {4, 5}, {3, 5, 4}, {3}};
for (int i = 0; i < freqs.length; ++i) {
mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
setCpuClusterFreqs(numClusters[i], numFreqs[i]);
- writeToFile(freqsLine(freqs[i]));
+ setFreqs(freqs[i]);
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
assertArrayEquals(freqs[i], actualFreqs);
final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
assertFalse(errMsg, mReader.perClusterTimesAvailable());
- // Verify that a second call won't read the proc file again
- assertTrue(mTestFile.delete());
+ // Verify that a second call won't re-read the freqs
+ clearFreqsAndData();
actualFreqs = mReader.readFreqs(mPowerProfile);
assertArrayEquals(freqs[i], actualFreqs);
assertFalse(errMsg, mReader.perClusterTimesAvailable());
@@ -125,17 +139,17 @@
final int[][] numFreqs = {{4}, {3, 5}, {3, 5, 4}};
for (int i = 0; i < freqs.length; ++i) {
mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
setCpuClusterFreqs(numClusters[i], numFreqs[i]);
- writeToFile(freqsLine(freqs[i]));
+ setFreqs(freqs[i]);
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
assertArrayEquals(freqs[i], actualFreqs);
final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
assertTrue(errMsg, mReader.perClusterTimesAvailable());
- // Verify that a second call won't read the proc file again
- assertTrue(mTestFile.delete());
+ // Verify that a second call won't re-read the freqs
+ clearFreqsAndData();
actualFreqs = mReader.readFreqs(mPowerProfile);
assertArrayEquals(freqs[i], actualFreqs);
assertTrue(errMsg, mReader.perClusterTimesAvailable());
@@ -147,7 +161,7 @@
final long[] freqs = {110, 123, 145, 167, 289, 997};
final long[][] times = increaseTime(new long[mUids.length][freqs.length]);
- writeToFile(freqsLine(freqs) + uidLines(mUids, times));
+ setFreqsAndData(freqs, times);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], times[i]);
@@ -155,14 +169,14 @@
mCallback.verifyNoMoreInteractions();
// Verify that readDelta also reads the frequencies if not already available.
- assertTrue(mTestFile.delete());
+ clearFreqsAndData();
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
assertArrayEquals(freqs, actualFreqs);
// Verify that a second call will only return deltas.
mCallback.clear();
final long[][] newTimes1 = increaseTime(times);
- writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes1));
+ setFreqsAndData(freqs, newTimes1);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], subtract(newTimes1[i], times[i]));
@@ -177,7 +191,7 @@
// Verify that calling with a null callback doesn't result in any crashes
mCallback.clear();
final long[][] newTimes2 = increaseTime(newTimes1);
- writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes2));
+ setFreqsAndData(freqs, newTimes2);
mReader.readDelta(null);
mCallback.verifyNoMoreInteractions();
@@ -185,13 +199,13 @@
// the previous call had null callback.
mCallback.clear();
final long[][] newTimes3 = increaseTime(newTimes2);
- writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes3));
+ setFreqsAndData(freqs, newTimes3);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], subtract(newTimes3[i], newTimes2[i]));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearFreqsAndData();
}
@Test
@@ -199,7 +213,7 @@
final long[] freqs = {110, 123, 145, 167, 289, 997};
final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
- writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+ setFreqsAndData(freqs, times1);
mReader.readAbsolute(mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], times1[i]);
@@ -207,20 +221,20 @@
mCallback.verifyNoMoreInteractions();
// Verify that readDelta also reads the frequencies if not already available.
- assertTrue(mTestFile.delete());
+ clearFreqsAndData();
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
assertArrayEquals(freqs, actualFreqs);
// Verify that a second call should still return absolute values
mCallback.clear();
final long[][] times2 = increaseTime(times1);
- writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+ setFreqsAndData(freqs, times2);
mReader.readAbsolute(mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], times2[i]);
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearFreqsAndData();
}
@Test
@@ -228,14 +242,14 @@
final long[] freqs = {110, 123, 145, 167, 289, 997};
final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
- writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+ setFreqsAndData(freqs, times1);
mReader.readDelta(mCallback);
// Verify that there should not be a callback for a particular UID if its time decreases.
mCallback.clear();
final long[][] times2 = increaseTime(times1);
times2[0][0] = 1000;
- writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+ setFreqsAndData(freqs, times2);
mReader.readDelta(mCallback);
for (int i = 1; i < mUids.length; i++) {
mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
@@ -246,7 +260,7 @@
mCallback.clear();
final long[][] times3 = increaseTime(times2);
times3[0] = increaseTime(times1)[0];
- writeToFile(freqsLine(freqs) + uidLines(mUids, times3));
+ setFreqsAndData(freqs, times3);
mReader.readDelta(mCallback);
mCallback.verify(mUids[0], subtract(times3[0], times1[0]));
for (int i = 1; i < mUids.length; i++) {
@@ -258,7 +272,7 @@
mCallback.clear();
final long[][] times4 = increaseTime(times3);
times4[0][0] *= -1;
- writeToFile(freqsLine(freqs) + uidLines(mUids, times4));
+ setFreqsAndData(freqs, times4);
mReader.readDelta(mCallback);
for (int i = 1; i < mUids.length; ++i) {
mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
@@ -269,14 +283,44 @@
mCallback.clear();
final long[][] times5 = increaseTime(times4);
times5[0] = increaseTime(times3)[0];
- writeToFile(freqsLine(freqs) + uidLines(mUids, times5));
+ setFreqsAndData(freqs, times5);
mReader.readDelta(mCallback);
mCallback.verify(mUids[0], subtract(times5[0], times3[0]));
for (int i = 1; i < mUids.length; i++) {
mCallback.verify(mUids[i], subtract(times5[i], times4[i]));
}
- assertTrue(mTestFile.delete());
+ clearFreqsAndData();
+ }
+
+ private void setFreqs(long[] freqs) throws IOException {
+ if (mUseBpf) {
+ mBpfMapReader.setFreqs(freqs);
+ } else {
+ writeToFile(freqsLine(freqs));
+ }
+ }
+
+ private void setFreqsAndData(long[] freqs, long[][] times) throws IOException {
+ if (mUseBpf) {
+ mBpfMapReader.setFreqs(freqs);
+ SparseArray<long[]> data = new SparseArray<>();
+ for (int i = 0; i < mUids.length; i++) {
+ data.put(mUids[i], times[i]);
+ }
+ mBpfMapReader.setData(data);
+ } else {
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times));
+ }
+ }
+
+ private void clearFreqsAndData() {
+ if (mUseBpf) {
+ mBpfMapReader.setFreqs(null);
+ mBpfMapReader.setData(new SparseArray<>());
+ } else {
+ assertTrue(mTestFile.delete());
+ }
}
private String freqsLine(long[] freqs) {
@@ -358,4 +402,36 @@
assertEquals(0, mData.size());
}
}
+
+ private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
+ private long[] mCpuFreqs;
+ private SparseArray<long[]> mNewData = new SparseArray<>();
+
+ public void setData(SparseArray<long[]> data) {
+ mNewData = data;
+ }
+
+ public void setFreqs(long[] freqs) {
+ mCpuFreqs = freqs;
+ }
+
+ @Override
+ public final boolean startTrackingBpfTimes() {
+ return true;
+ }
+
+ @Override
+ protected final boolean readBpfData() {
+ if (!mUseBpf) {
+ return false;
+ }
+ mData = mNewData;
+ return true;
+ }
+
+ @Override
+ public final long[] getDataDimensions() {
+ return mCpuFreqs;
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
index 479e19e..dac35e5 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
@@ -31,20 +31,33 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
+import java.util.Collection;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
public class KernelSingleUidTimeReaderTest {
private final static int TEST_UID = 2222;
private final static int TEST_FREQ_COUNT = 5;
private KernelSingleUidTimeReader mReader;
private TestInjector mInjector;
+ protected boolean mUseBpf;
+
+ @Parameters(name="useBpf={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { {true}, {false} });
+ }
+
+ public KernelSingleUidTimeReaderTest(boolean useBpf) {
+ mUseBpf = useBpf;
+ }
@Before
public void setUp() {
@@ -273,6 +286,7 @@
class TestInjector extends Injector {
private byte[] mData;
+ private long[] mBpfData;
private boolean mThrowExcpetion;
@Override
@@ -284,6 +298,14 @@
}
}
+ @Override
+ public long[] readBpfData(int uid) {
+ if (!mUseBpf || mBpfData == null) {
+ return new long[0];
+ }
+ return mBpfData;
+ }
+
public void setData(long[] cpuTimes) {
final ByteBuffer buffer = ByteBuffer.allocate(cpuTimes.length * Long.BYTES);
buffer.order(ByteOrder.nativeOrder());
@@ -291,6 +313,7 @@
buffer.putLong(time / 10);
}
mData = buffer.array();
+ mBpfData = cpuTimes.clone();
}
public void letReadDataThrowException(boolean throwException) {
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 9c2e95f..c1e7a36 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -2243,6 +2243,19 @@
return nativeCreateGraphicBufferHandle(mNativePtr);
}
+ /**
+ * @return {@link HardwareBuffer} which is internally used by hardware bitmap
+ *
+ * Note: the HardwareBuffer does *not* have an associated {@link ColorSpace}.
+ * To render this object the same as its rendered with this Bitmap, you
+ * should also call {@link getColorSpace}.
+ *
+ * @hide
+ */
+ public HardwareBuffer getHardwareBuffer() {
+ return nativeGetHardwareBuffer(mNativePtr);
+ }
+
//////////// native methods
private static native Bitmap nativeCreate(int[] colors, int offset,
@@ -2308,6 +2321,7 @@
private static native Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
long nativeColorSpace);
private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap);
+ private static native HardwareBuffer nativeGetHardwareBuffer(long nativeBitmap);
private static native ColorSpace nativeComputeColorSpace(long nativePtr);
private static native void nativeSetColorSpace(long nativePtr, long nativeColorSpace);
private static native boolean nativeIsSRGB(long nativePtr);
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index cefc9db..3a771bb 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -19,14 +19,20 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.hardware.cas.V1_0.HidlCasPluginDescriptor;
import android.hardware.cas.V1_0.ICas;
import android.hardware.cas.V1_0.IMediaCasService;
import android.hardware.cas.V1_2.ICasListener;
+import android.hardware.cas.V1_2.Status;
import android.media.MediaCasException.*;
import android.media.tv.TvInputService.PriorityHintUseCaseType;
+import android.media.tv.tunerresourcemanager.CasSessionRequest;
+import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerResourceManager;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IHwBinder;
import android.os.Looper;
@@ -39,6 +45,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
/**
* MediaCas can be used to obtain keys for descrambling protected media streams, in
@@ -110,6 +119,10 @@
private EventHandler mEventHandler;
private @PriorityHintUseCaseType int mPriorityHint;
private String mTvInputServiceSessionId;
+ private int mClientId;
+ private int mCasSystemId;
+ private TunerResourceManager mTunerResourceManager = null;
+ private final Map<Session, Integer> mSessionMap = new HashMap<>();
/**
* Scrambling modes used to open cas sessions.
@@ -329,6 +342,10 @@
createFromSessionId(sessionId), msg.arg1, msg.arg2,
bundle.getByteArray(DATA_KEY));
} else if (msg.what == MSG_CAS_STATUS_EVENT) {
+ if ((msg.arg1 == PLUGIN_STATUS_SESSION_NUMBER_CHANGED)
+ && (mTunerResourceManager != null)) {
+ mTunerResourceManager.updateCasInfo(mCasSystemId, msg.arg2);
+ }
mListener.onPluginStatusUpdate(MediaCas.this, msg.arg1, msg.arg2);
} else if (msg.what == MSG_CAS_RESOURCE_LOST) {
mListener.onResourceLost(MediaCas.this);
@@ -364,6 +381,19 @@
EventHandler.MSG_CAS_STATUS_EVENT, status, arg));
}
};
+
+ private final TunerResourceManager.ResourcesReclaimListener mResourceListener =
+ new TunerResourceManager.ResourcesReclaimListener() {
+ @Override
+ public void onReclaimResources() {
+ synchronized (mSessionMap) {
+ mSessionMap.forEach((casSession, sessionResourceId) -> casSession.close());
+ }
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
+ EventHandler.MSG_CAS_RESOURCE_LOST));
+ }
+ };
+
/**
* Describe a CAS plugin with its CA_system_ID and string name.
*
@@ -429,11 +459,21 @@
*/
public final class Session implements AutoCloseable {
final ArrayList<Byte> mSessionId;
+ boolean mIsClosed = false;
Session(@NonNull ArrayList<Byte> sessionId) {
mSessionId = new ArrayList<Byte>(sessionId);
}
+ private void validateSessionInternalStates() {
+ if (mICas == null) {
+ throw new IllegalStateException();
+ }
+ if (mIsClosed) {
+ MediaCasStateException.throwExceptionIfNeeded(Status.ERROR_CAS_SESSION_NOT_OPENED);
+ }
+ }
+
/**
* Query if an object equal current Session object.
*
@@ -459,7 +499,7 @@
*/
public void setPrivateData(@NonNull byte[] data)
throws MediaCasException {
- validateInternalStates();
+ validateSessionInternalStates();
try {
MediaCasException.throwExceptionIfNeeded(
@@ -483,7 +523,7 @@
*/
public void processEcm(@NonNull byte[] data, int offset, int length)
throws MediaCasException {
- validateInternalStates();
+ validateSessionInternalStates();
try {
MediaCasException.throwExceptionIfNeeded(
@@ -522,7 +562,7 @@
*/
public void sendSessionEvent(int event, int arg, @Nullable byte[] data)
throws MediaCasException {
- validateInternalStates();
+ validateSessionInternalStates();
if (mICasV11 == null) {
Log.d(TAG, "Send Session Event isn't supported by cas@1.0 interface");
@@ -546,7 +586,7 @@
*/
@NonNull
public byte[] getSessionId() {
- validateInternalStates();
+ validateSessionInternalStates();
return toBytes(mSessionId);
}
@@ -558,11 +598,12 @@
*/
@Override
public void close() {
- validateInternalStates();
-
+ validateSessionInternalStates();
try {
MediaCasStateException.throwExceptionIfNeeded(
mICas.closeSession(mSessionId));
+ mIsClosed = true;
+ removeSessionFromResourceMap(this);
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
@@ -664,6 +705,7 @@
/**
* Instantiate a CA system of the specified system id.
*
+ * @param context the context of the caller.
* @param casSystemId The system id of the CA system.
* @param tvInputServiceSessionId The Id of the session opened in TV Input Service (TIS)
* {@link android.media.tv.TvInputService#onCreateSession(String, String)}
@@ -672,11 +714,23 @@
* @throws UnsupportedCasException if the device does not support the
* specified CA system.
*/
- public MediaCas(int casSystemId, @Nullable String tvInputServiceSessionId,
- @PriorityHintUseCaseType int priorityHint) throws UnsupportedCasException {
+ public MediaCas(@NonNull Context context, int casSystemId,
+ @Nullable String tvInputServiceSessionId,
+ @PriorityHintUseCaseType int priorityHint) throws UnsupportedCasException {
this(casSystemId);
- mPriorityHint = priorityHint;
- mTvInputServiceSessionId = tvInputServiceSessionId;
+
+ Objects.requireNonNull(context, "context must not be null");
+ mCasSystemId = casSystemId;
+ mTunerResourceManager = (TunerResourceManager)
+ context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE);
+ if (mTunerResourceManager != null) {
+ int[] clientId = new int[1];
+ ResourceClientProfile profile =
+ new ResourceClientProfile(tvInputServiceSessionId, priorityHint);
+ mTunerResourceManager.registerClientProfile(
+ profile, new HandlerExecutor(mEventHandler), mResourceListener, clientId);
+ mClientId = clientId[0];
+ }
}
IHwBinder getBinder() {
@@ -813,6 +867,40 @@
}
}
+ private int getSessionResourceId() throws MediaCasException {
+ validateInternalStates();
+
+ int[] sessionResourceId = new int[1];
+ sessionResourceId[0] = -1;
+ if (mTunerResourceManager != null) {
+ CasSessionRequest casSessionRequest = new CasSessionRequest(mClientId, mCasSystemId);
+ if (!mTunerResourceManager.requestCasSession(casSessionRequest, sessionResourceId)) {
+ throw new MediaCasException.ResourceBusyException(
+ "insufficient resource to Open Session");
+ }
+ }
+ return sessionResourceId[0];
+ }
+
+ private void addSessionToResourceMap(Session session, int sessionResourceId) {
+
+ if (sessionResourceId != -1) {
+ synchronized (mSessionMap) {
+ mSessionMap.put(session, sessionResourceId);
+ }
+ }
+ }
+
+ private void removeSessionFromResourceMap(Session session) {
+
+ synchronized (mSessionMap) {
+ if (mSessionMap.get(session) != null) {
+ mTunerResourceManager.releaseCasSession(mSessionMap.get(session));
+ mSessionMap.remove(session);
+ }
+ }
+ }
+
/**
* Open a session to descramble one or more streams scrambled by the
* conditional access system.
@@ -824,12 +912,13 @@
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
public Session openSession() throws MediaCasException {
- validateInternalStates();
+ int sessionResourceId = getSessionResourceId();
try {
OpenSessionCallback cb = new OpenSessionCallback();
mICas.openSession(cb);
MediaCasException.throwExceptionIfNeeded(cb.mStatus);
+ addSessionToResourceMap(cb.mSession, sessionResourceId);
return cb.mSession;
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
@@ -853,7 +942,7 @@
@Nullable
public Session openSession(@SessionUsage int sessionUsage, @ScramblingMode int scramblingMode)
throws MediaCasException {
- validateInternalStates();
+ int sessionResourceId = getSessionResourceId();
if (mICasV12 == null) {
Log.d(TAG, "Open Session with scrambling mode is only supported by cas@1.2+ interface");
@@ -864,6 +953,7 @@
OpenSession_1_2_Callback cb = new OpenSession_1_2_Callback();
mICasV12.openSession_1_2(sessionUsage, scramblingMode, cb);
MediaCasException.throwExceptionIfNeeded(cb.mStatus);
+ addSessionToResourceMap(cb.mSession, sessionResourceId);
return cb.mSession;
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
@@ -987,6 +1077,16 @@
mICas = null;
}
}
+
+ if (mTunerResourceManager != null) {
+ mTunerResourceManager.unregisterClientProfile(mClientId);
+ mTunerResourceManager = null;
+ }
+
+ if (mHandlerThread != null) {
+ mHandlerThread.quit();
+ mHandlerThread = null;
+ }
}
@Override
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 62c7e51..b579144 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -111,37 +111,32 @@
public @interface PriorityHintUseCaseType {}
/**
- * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}:
- * Background.
- * TODO Link: Tuner#Tuner(Context, string, int).
+ * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String,
+ * int)}: Background. TODO Link: Tuner#Tuner(Context, string, int).
*/
public static final int PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND = 100;
/**
- * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}:
- * Scan.
- * TODO Link: Tuner#Tuner(Context, string, int).
+ * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String,
+ * int)}: Scan. TODO Link: Tuner#Tuner(Context, string, int).
*/
public static final int PRIORITY_HINT_USE_CASE_TYPE_SCAN = 200;
/**
- * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}:
- * Playback.
- * TODO Link: Tuner#Tuner(Context, string, int).
+ * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String,
+ * int)}: Playback. TODO Link: Tuner#Tuner(Context, string, int).
*/
public static final int PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK = 300;
/**
- * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}:
- * Live.
- * TODO Link: Tuner#Tuner(Context, string, int).
+ * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String,
+ * int)}: Live. TODO Link: Tuner#Tuner(Context, string, int).
*/
public static final int PRIORITY_HINT_USE_CASE_TYPE_LIVE = 400;
/**
- * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}:
- * Record.
- * TODO Link: Tuner#Tuner(Context, string, int).
+ * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String,
+ * int)}: Record. TODO Link: Tuner#Tuner(Context, string, int).
*/
public static final int PRIORITY_HINT_USE_CASE_TYPE_RECORD = 500;
diff --git a/media/java/android/media/tv/tuner/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java
index 364516c..0f5bf08 100644
--- a/media/java/android/media/tv/tuner/DemuxCapabilities.java
+++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java
@@ -18,7 +18,7 @@
import android.annotation.BytesLong;
import android.annotation.IntDef;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
import android.annotation.Size;
import android.annotation.SystemApi;
import android.media.tv.tuner.filter.Filter;
@@ -159,7 +159,7 @@
* {@link FilterConfiguration}.
* <p>The ith element represents the filter's capability as the source for the ith type.
*/
- @Nullable
+ @NonNull
@Size(5)
public int[] getLinkCapabilities() {
return mLinkCaps;
diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java
index f46d3ce..40add56 100644
--- a/media/java/android/media/tv/tuner/Descrambler.java
+++ b/media/java/android/media/tv/tuner/Descrambler.java
@@ -17,12 +17,15 @@
package android.media.tv.tuner;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.media.tv.tuner.TunerConstants.Result;
import android.media.tv.tuner.filter.Filter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* This class is used to interact with descramblers.
@@ -75,6 +78,7 @@
* @param filter an optional filter instance to identify upper stream.
* @return result status of the operation.
*/
+ @Result
public int addPid(@PidType int pidType, int pid, @Nullable Filter filter) {
return nativeAddPid(pidType, pid, filter);
}
@@ -89,6 +93,7 @@
* @param filter an optional filter instance to identify upper stream.
* @return result status of the operation.
*/
+ @Result
public int removePid(@PidType int pidType, int pid, @Nullable Filter filter) {
return nativeRemovePid(pidType, pid, filter);
}
@@ -102,7 +107,9 @@
* @param keyToken the token to be used to link the key slot.
* @return result status of the operation.
*/
- public int setKeyToken(@Nullable byte[] keyToken) {
+ @Result
+ public int setKeyToken(@NonNull byte[] keyToken) {
+ Objects.requireNonNull(keyToken, "key token must not be null");
return nativeSetKeyToken(keyToken);
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index bd00201..012b8d6 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -38,6 +38,7 @@
import android.media.tv.tuner.frontend.FrontendInfo;
import android.media.tv.tuner.frontend.FrontendSettings;
import android.media.tv.tuner.frontend.FrontendStatus;
+import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType;
import android.media.tv.tuner.frontend.OnTuneEventListener;
import android.media.tv.tuner.frontend.ScanCallback;
import android.os.Handler;
@@ -45,6 +46,7 @@
import android.os.Message;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -88,6 +90,10 @@
private ScanCallback mScanCallback;
@Nullable
private Executor mScanCallbackExecutor;
+ @Nullable
+ private OnResourceLostListener mOnResourceLostListener;
+ @Nullable
+ private Executor mOnResourceLostListenerExecutor;
/**
* Constructs a Tuner instance.
@@ -97,14 +103,37 @@
* @param useCase the use case of this Tuner instance.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
- public Tuner(@NonNull Context context, @NonNull String tvInputSessionId,
- @TvInputService.PriorityHintUseCaseType int useCase,
- @Nullable OnResourceLostListener listener) {
+ public Tuner(@NonNull Context context, @Nullable String tvInputSessionId,
+ @TvInputService.PriorityHintUseCaseType int useCase) {
nativeSetup();
mContext = context;
}
/**
+ * Sets the listener for resource lost.
+ *
+ * @param executor the executor on which the listener should be invoked.
+ * @param listener the listener that will be run.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ public void setResourceLostListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OnResourceLostListener listener) {
+ Objects.requireNonNull(executor, "OnResourceLostListener must not be null");
+ Objects.requireNonNull(listener, "executor must not be null");
+ mOnResourceLostListener = listener;
+ mOnResourceLostListenerExecutor = executor;
+ }
+
+ /**
+ * Removes the listener for resource lost.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ public void clearResourceLostListener() {
+ mOnResourceLostListener = null;
+ mOnResourceLostListenerExecutor = null;
+ }
+
+ /**
* Shares the frontend resource with another Tuner instance
*
* @param tuner the Tuner instance to share frontend resource with.
@@ -387,7 +416,7 @@
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
- public int setLna(boolean enable) {
+ public int setLnaEnabled(boolean enable) {
TunerUtils.checkTunerPermission(mContext);
return nativeSetLna(enable);
}
@@ -398,10 +427,10 @@
* <p>This retrieve the statuses of the frontend for given status types.
*
* @param statusTypes an array of status types which the caller requests.
- * @return statuses which response the caller's requests.
+ * @return statuses which response the caller's requests. {@code null} if the operation failed.
*/
@Nullable
- public FrontendStatus getFrontendStatus(@NonNull int[] statusTypes) {
+ public FrontendStatus getFrontendStatus(@NonNull @FrontendStatusType int[] statusTypes) {
return nativeGetFrontendStatus(statusTypes);
}
@@ -482,6 +511,10 @@
/**
* Gets Demux capabilities.
+ *
+ * @param context the context of the caller.
+ * @return A {@link DemuxCapabilities} instance that represents the demux capabilities.
+ * {@code null} if the operation failed.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
@@ -637,13 +670,15 @@
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
- public Lnb openLnb(@CallbackExecutor @Nullable Executor executor, @Nullable LnbCallback cb) {
+ public Lnb openLnb(@CallbackExecutor @NonNull Executor executor, @NonNull LnbCallback cb) {
+ Objects.requireNonNull(executor, "executor must not be null");
+ Objects.requireNonNull(cb, "LnbCallback must not be null");
TunerUtils.checkTunerPermission(mContext);
return openLnbByName(null, executor, cb);
}
/**
- * Opens an LNB (low-noise block downconverter) object.
+ * Opens an LNB (low-noise block downconverter) object specified by the give name.
*
* @param name the LNB name.
* @param executor the executor on which callback will be invoked. The default event handler
@@ -653,8 +688,10 @@
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
- public Lnb openLnbByName(@Nullable String name, @CallbackExecutor @Nullable Executor executor,
+ public Lnb openLnbByName(@NonNull String name, @CallbackExecutor @NonNull Executor executor,
@NonNull LnbCallback cb) {
+ Objects.requireNonNull(executor, "executor must not be null");
+ Objects.requireNonNull(cb, "LnbCallback must not be null");
TunerUtils.checkTunerPermission(mContext);
// TODO: use resource manager to get LNB ID.
return new Lnb(0);
@@ -718,8 +755,10 @@
@Nullable
public DvrRecorder openDvrRecorder(
@BytesLong long bufferSize,
- @CallbackExecutor @Nullable Executor executor,
- @Nullable OnRecordStatusChangedListener l) {
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull OnRecordStatusChangedListener l) {
+ Objects.requireNonNull(executor, "executor must not be null");
+ Objects.requireNonNull(l, "OnRecordStatusChangedListener must not be null");
TunerUtils.checkTunerPermission(mContext);
DvrRecorder dvr = nativeOpenDvrRecorder(bufferSize);
return dvr;
@@ -739,8 +778,10 @@
@Nullable
public DvrPlayback openDvrPlayback(
@BytesLong long bufferSize,
- @CallbackExecutor @Nullable Executor executor,
- @Nullable OnPlaybackStatusChangedListener l) {
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull OnPlaybackStatusChangedListener l) {
+ Objects.requireNonNull(executor, "executor must not be null");
+ Objects.requireNonNull(l, "OnPlaybackStatusChangedListener must not be null");
TunerUtils.checkTunerPermission(mContext);
DvrPlayback dvr = nativeOpenDvrPlayback(bufferSize);
return dvr;
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index 8105c74..63bc248 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -53,7 +53,7 @@
void onInputStreamIds(@NonNull int[] inputStreamIds);
/** Locked signal standard for DVBS. */
- void onDvbsStandard(@DvbsFrontendSettings.Standard int dvbsStandandard);
+ void onDvbsStandard(@DvbsFrontendSettings.Standard int dvbsStandard);
/** Locked signal standard. for DVBT */
void onDvbtStandard(@DvbtFrontendSettings.Standard int dvbtStandard);
diff --git a/media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java b/media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java
index cbfbf77..afdbce0 100644
--- a/media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java
+++ b/media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java
@@ -48,13 +48,13 @@
@Test
public void testTunerConstructor() throws Exception {
- Tuner tuner = new Tuner(mContext, "123", 1, null);
+ Tuner tuner = new Tuner(mContext, "123", 1);
assertNotNull(tuner);
}
@Test
public void testOpenDescrambler() throws Exception {
- Tuner tuner = new Tuner(mContext, "123", 1, null);
+ Tuner tuner = new Tuner(mContext, "123", 1);
Descrambler descrambler = tuner.openDescrambler();
assertNotNull(descrambler);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 585acfe..a0d5a1b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -43,9 +43,11 @@
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.volume.CarVolumeDialogComponent;
@@ -74,10 +76,14 @@
@Singleton
@Provides
- static HeadsUpManagerPhone provideHeadsUpManagerPhone(Context context,
+ static HeadsUpManagerPhone provideHeadsUpManagerPhone(
+ Context context,
StatusBarStateController statusBarStateController,
- KeyguardBypassController bypassController) {
- return new HeadsUpManagerPhone(context, statusBarStateController, bypassController);
+ KeyguardBypassController bypassController,
+ NotificationGroupManager groupManager,
+ ConfigurationController configurationController) {
+ return new HeadsUpManagerPhone(context, statusBarStateController, bypassController,
+ groupManager, configurationController);
}
@Binds
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 5af3ad5..a4eada4 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -128,6 +128,7 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
+import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -324,6 +325,7 @@
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
DismissCallbackRegistry dismissCallbackRegistry,
+ StatusBarTouchableRegionManager statusBarTouchableRegionManager,
/* Car Settings injected components. */
CarServiceProvider carServiceProvider,
Lazy<PowerManagerHelper> powerManagerHelperLazy,
@@ -405,7 +407,8 @@
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
- dismissCallbackRegistry);
+ dismissCallbackRegistry,
+ statusBarTouchableRegionManager);
mUserSwitcherController = userSwitcherController;
mScrimController = scrimController;
mLockscreenLockIconController = lockscreenLockIconController;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 7f64990..7294965 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -87,6 +87,7 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
+import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneDependenciesModule;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -197,6 +198,7 @@
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
DismissCallbackRegistry dismissCallbackRegistry,
+ StatusBarTouchableRegionManager statusBarTouchableRegionManager,
CarServiceProvider carServiceProvider,
Lazy<PowerManagerHelper> powerManagerHelperLazy,
FullscreenUserSwitcher fullscreenUserSwitcher,
@@ -277,6 +279,7 @@
extensionController,
userInfoControllerImpl,
dismissCallbackRegistry,
+ statusBarTouchableRegionManager,
carServiceProvider,
powerManagerHelperLazy,
fullscreenUserSwitcher,
diff --git a/packages/SettingsProvider/res/values-af/strings.xml b/packages/SettingsProvider/res/values-af/strings.xml
index 8c2f8b3..24efbb6 100644
--- a/packages/SettingsProvider/res/values-af/strings.xml
+++ b/packages/SettingsProvider/res/values-af/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Instellingsberging"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Veranderings aan jou warmkolinstellings"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Jou warmkolband het verander."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Hierdie toestel steun nie jou voorkeur vir net 5 GHz nie. Hierdie toestel sal pleks daarvan die 5 GHz-band gebruik wanneer dit beskikbaar is."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Warmkolinstellings het verander"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Tik om besonderhede te sien"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-am/strings.xml b/packages/SettingsProvider/res/values-am/strings.xml
index 640301a..48fb705 100644
--- a/packages/SettingsProvider/res/values-am/strings.xml
+++ b/packages/SettingsProvider/res/values-am/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"የቅንብሮች ማከማቻ"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"በእርስዎ ሆትስፖት ቅንብሮች ላይ ለውጦች"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"የእርስዎ ሆትስፖት ባንድ ተለውጧል።"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"ይህ መሣሪያ የእርስዎን ምርጫ ለ5GHz ብቻ አይደግፍም። በምትኩ፣ ይህ መሣሪያ ሲገኝ 5GHz ባንድ ይጠቀማል።"</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"የመገናኛ ነጥብ ቅንብሮች ተለውጠዋል"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"ዝርዝሮችን ለማየት መታ ያድርጉ"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-ar/strings.xml b/packages/SettingsProvider/res/values-ar/strings.xml
index 2675839..6371f2c 100644
--- a/packages/SettingsProvider/res/values-ar/strings.xml
+++ b/packages/SettingsProvider/res/values-ar/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"تخزين الإعدادات"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"التغييرات التي طرأت على إعدادات نقطة الاتصال"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"تمّ تغيير نطاق نقطة الاتصال الخاصة بك."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"لا يتوافق هذا الجهاز مع إعدادك المفضّل الخاص باستخدام النطاق 5 غيغاهرتز فقط. وسيستخدم الجهاز بدلاً من ذلك النطاق 5 غيغاهرتز عندما يكون متاحًا."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"تم تغيير إعدادات نقطة الاتصال."</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"انقر للاطّلاع على التفاصيل."</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-as/strings.xml b/packages/SettingsProvider/res/values-as/strings.xml
index b70146a..5235e3c 100644
--- a/packages/SettingsProvider/res/values-as/strings.xml
+++ b/packages/SettingsProvider/res/values-as/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"ছেটিংছসমূহৰ সঞ্চয়াগাৰ"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"আপোনাৰ হটস্পট ছেটিংসমূহত হোৱা সালসলনিসমূহ"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"আপোনাৰ হটস্পটৰ বেণ্ড সলনি কৰা হৈছে।"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"আপোনাৰ কেৱল ৫ গিগাহাৰ্টজৰ প্ৰতি অগ্ৰাধিকাৰ এই ডিভাচইচটোৱে সমৰ্থন নকৰে। ইয়াৰ পৰিৱৰ্তে, ডিভাচইচটোৱে যেতিয়া ৫ গিগাহাৰ্টজ বেণ্ড উপলব্ধ হ’ব তেতিয়া তাক ব্যৱহাৰ কৰিব।"</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-az/strings.xml b/packages/SettingsProvider/res/values-az/strings.xml
index 8b4f75b..b0e8642 100644
--- a/packages/SettingsProvider/res/values-az/strings.xml
+++ b/packages/SettingsProvider/res/values-az/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Ayarlar Deposu"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Hotspot ayarlarınızda dəyişiklik"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Hotspot diapazonu dəyişib."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Bu cihaz yalnız 5GHz üçün tərcihinizi dəstəkləmir. Əvəzində, əlçatan olduqda bu cihaz 5GHz diapazonundan istifadə edəcək."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Hotspot ayarları dəyişib"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Detalları görmək üçün toxunun"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-b+sr+Latn/strings.xml b/packages/SettingsProvider/res/values-b+sr+Latn/strings.xml
index 9c3a00e..def4b68 100644
--- a/packages/SettingsProvider/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsProvider/res/values-b+sr+Latn/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Podešavanja skladišta"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Promene podešavanja za hotspot"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Opseg hotspota je promenjen."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Ovaj uređaj ne podržava podešavanje samo za 5 GHz. Uređaj će koristiti opseg od 5 GHz kada bude dostupan."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Podešavanja hotspota su promenjena"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Dodirnite da biste videli detalje"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-be/strings.xml b/packages/SettingsProvider/res/values-be/strings.xml
index 0e098e7..709178e 100644
--- a/packages/SettingsProvider/res/values-be/strings.xml
+++ b/packages/SettingsProvider/res/values-be/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Сховішча налад"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Змяненні ў наладах хот-спота"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Частата хот-спота змянілася."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Прылада не можа працаваць толькі на частаце 5 ГГц. Гэта частата будзе выкарыстоўвацца, калі гэта магчыма."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Налады хот-спота змяніліся"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Дакраніцеся, каб убачыць падрабязныя звесткі"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-bg/strings.xml b/packages/SettingsProvider/res/values-bg/strings.xml
index 30526f2..b2eae73 100644
--- a/packages/SettingsProvider/res/values-bg/strings.xml
+++ b/packages/SettingsProvider/res/values-bg/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Настройки за хранилище"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Промени в настройките ви за точка за достъп"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Честотната лента на точката ви за достъп е променена."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Това устройство не поддържа предпочитанието ви за използване само на честотната лента от 5 ГХц. Вместо това то ще я ползва, когато е възможно."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Настройките за точката за достъп са променени"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Докоснете, за да видите подробности"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-bn/strings.xml b/packages/SettingsProvider/res/values-bn/strings.xml
index 8fc6bbb..c785cd8 100644
--- a/packages/SettingsProvider/res/values-bn/strings.xml
+++ b/packages/SettingsProvider/res/values-bn/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"সেটিংস স্টোরেজ"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"আপনার হটস্পট সেটিংসে করা পরিবর্তনগুলি"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"আপনার হটস্পট ব্যান্ড পরিবর্তন করা হয়েছে।"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"এই ডিভাইসটি শুধুমাত্র 5GHz এর জন্য আপনার পছন্দ সমর্থন করে না। পরিবর্তে, এই ডিভাইসটি 5GHz ব্যান্ড ব্যবহার করবে।"</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-bs/strings.xml b/packages/SettingsProvider/res/values-bs/strings.xml
index ddacb32..506fea2 100644
--- a/packages/SettingsProvider/res/values-bs/strings.xml
+++ b/packages/SettingsProvider/res/values-bs/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Postavke za pohranu podataka"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Promjene postavki pristupne tačke"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Opseg pristupne tačke je promijenjen."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Ovaj uređaj ne podržava vašu postavku za mreže od isključivo 5 GHz. Uređaj će koristiti opseg of 5 GHz kada bude dostupan."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Postavke pristupne tačke su promijenjene"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Dodirnite da vidite detalje"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-ca/strings.xml b/packages/SettingsProvider/res/values-ca/strings.xml
index 0c2ad73..58d3572e 100644
--- a/packages/SettingsProvider/res/values-ca/strings.xml
+++ b/packages/SettingsProvider/res/values-ca/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Configuració de l\'emmagatzematge"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Canvis en la configuració del punt d\'accés Wi‑Fi"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Ha canviat la teva banda del punt d\'accés Wi‑Fi."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Aquest dispositiu no admet utilitzar exclusivament una banda de 5 GHz. El dispositiu utilitzarà una banda de 5 GHz quan estigui disponible."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"La configuració del punt d\'accés Wi‑Fi ha canviat"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Toca per veure\'n els detalls"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-cs/strings.xml b/packages/SettingsProvider/res/values-cs/strings.xml
index ab474b1..449c632 100644
--- a/packages/SettingsProvider/res/values-cs/strings.xml
+++ b/packages/SettingsProvider/res/values-cs/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Paměť pro nastavení"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Změny nastavení hotspotu"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Pásmo hotspotu se změnilo."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Toto zařízení nepodporuje vaše nastavení jen 5GHz pásma. Zařízení použije pásmo 5 GHz, jen když bude dostupné."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Nastavení hotspotu se změnilo"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Klepnutím zobrazíte podrobnosti"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-da/strings.xml b/packages/SettingsProvider/res/values-da/strings.xml
index 719614c..278d978 100644
--- a/packages/SettingsProvider/res/values-da/strings.xml
+++ b/packages/SettingsProvider/res/values-da/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Lagring af indstillinger"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Ændringer af dine indstillinger for hotspot"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Dit hotspotbånd er ændret."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Denne enhed understøtter ikke din præference om kun 5 GHz. Denne enhed vil i stedet bruge 5 GHz-båndet, når det er muligt."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Indstillingerne for hotspots har ændret sig"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Tryk for at se flere oplysninger"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-de/strings.xml b/packages/SettingsProvider/res/values-de/strings.xml
index 6e253e0..a469936 100644
--- a/packages/SettingsProvider/res/values-de/strings.xml
+++ b/packages/SettingsProvider/res/values-de/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Einstellungsspeicher"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Änderungen an deinen Hotspot-Einstellungen"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Dein Hotspot-Band hat sich geändert."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Dieses Gerät unterstützt die ausschließliche Nutzung von 5 GHz nicht. Es greift aber immer auf das 5-GHz-Band zurück, wenn dieses verfügbar ist."</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-el/strings.xml b/packages/SettingsProvider/res/values-el/strings.xml
index c47fea2..1bfbf27 100644
--- a/packages/SettingsProvider/res/values-el/strings.xml
+++ b/packages/SettingsProvider/res/values-el/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Αποθηκευτικός χώρος ρυθμίσεων"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Αλλαγές στις ρυθμίσεις σημείου πρόσβασης Wi-Fi"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Το εύρος σημείου πρόσβασης Wi-Fi άλλαξε."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Αυτή η συσκευή δεν υποστηρίζει την προτίμησή σας για αποκλειστική χρήση του εύρους 5 GHz. Αντ\' αυτού, αυτή η συσκευή θα χρησιμοποιεί το εύρος 5 GHz όταν είναι διαθέσιμο."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Οι ρυθμίσεις σημείου πρόσβασης Wi-Fi έχουν αλλάξει"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Πατήστε για προβολή λεπτομερειών."</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-en-rAU/strings.xml b/packages/SettingsProvider/res/values-en-rAU/strings.xml
index fac51d83..4e90cad 100644
--- a/packages/SettingsProvider/res/values-en-rAU/strings.xml
+++ b/packages/SettingsProvider/res/values-en-rAU/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Settings Storage"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Changes to your hotspot settings"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Your hotspot band has changed."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Hotspot settings have changed"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Tap to see details"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-en-rCA/strings.xml b/packages/SettingsProvider/res/values-en-rCA/strings.xml
index fac51d83..4e90cad 100644
--- a/packages/SettingsProvider/res/values-en-rCA/strings.xml
+++ b/packages/SettingsProvider/res/values-en-rCA/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Settings Storage"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Changes to your hotspot settings"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Your hotspot band has changed."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Hotspot settings have changed"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Tap to see details"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-en-rGB/strings.xml b/packages/SettingsProvider/res/values-en-rGB/strings.xml
index fac51d83..4e90cad 100644
--- a/packages/SettingsProvider/res/values-en-rGB/strings.xml
+++ b/packages/SettingsProvider/res/values-en-rGB/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Settings Storage"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Changes to your hotspot settings"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Your hotspot band has changed."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Hotspot settings have changed"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Tap to see details"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-en-rIN/strings.xml b/packages/SettingsProvider/res/values-en-rIN/strings.xml
index fac51d83..4e90cad 100644
--- a/packages/SettingsProvider/res/values-en-rIN/strings.xml
+++ b/packages/SettingsProvider/res/values-en-rIN/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Settings Storage"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Changes to your hotspot settings"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Your hotspot band has changed."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Hotspot settings have changed"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Tap to see details"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-en-rXC/strings.xml b/packages/SettingsProvider/res/values-en-rXC/strings.xml
index fbc348b..4ea5c57 100644
--- a/packages/SettingsProvider/res/values-en-rXC/strings.xml
+++ b/packages/SettingsProvider/res/values-en-rXC/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Settings Storage"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Changes to your hotspot settings"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Your hotspot band has changed."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"This device doesn’t support your preference for 5GHz only. Instead, this device will use the 5GHz band when available."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Hotspot settings have changed"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Tap to see details"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-es-rUS/strings.xml b/packages/SettingsProvider/res/values-es-rUS/strings.xml
index af90257..4345b7a 100644
--- a/packages/SettingsProvider/res/values-es-rUS/strings.xml
+++ b/packages/SettingsProvider/res/values-es-rUS/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Almacenamiento de configuración"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Cambios en la configuración de tu hotspot"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Cambió la banda de tu hotspot."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Si bien este dispositivo no admite la opción para conectarse exclusivamente a bandas de 5 GHz, las usará cuando estén disponibles."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Se modificó la configuración de hotspot"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Presiona para obtener más información"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-es/strings.xml b/packages/SettingsProvider/res/values-es/strings.xml
index ff4ee38..3f1fa61 100644
--- a/packages/SettingsProvider/res/values-es/strings.xml
+++ b/packages/SettingsProvider/res/values-es/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Almacenamiento de configuración"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Cambios en los ajustes del punto de acceso"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"La banda de tu punto de acceso ha cambiado."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Este dispositivo no admite la opción de conectarse únicamente a bandas de 5 GHz, pero las usará cuando estén disponibles."</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-et/strings.xml b/packages/SettingsProvider/res/values-et/strings.xml
index a0ec593..856ccf1 100644
--- a/packages/SettingsProvider/res/values-et/strings.xml
+++ b/packages/SettingsProvider/res/values-et/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Seadete talletusruum"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Muudatused teie kuumkoha seadetes"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Teie kuumkoha sagedusriba on muutunud."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"See seade ei toeta teie eelistatud ainult 5 GHz riba. Seade kasutab 5 GHz riba ainult siis, kui see on saadaval."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Kuumkoha seaded on muutunud"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Puudutage üksikasjade vaatamiseks"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-eu/strings.xml b/packages/SettingsProvider/res/values-eu/strings.xml
index 220b486..7ca91d8 100644
--- a/packages/SettingsProvider/res/values-eu/strings.xml
+++ b/packages/SettingsProvider/res/values-eu/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Ezarpenen biltegia"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Aldaketak egin dira sare publikoaren ezarpenetan"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Aldatu da sare publikoaren banda."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Gailuak ez du onartzen 5 GHz-ko banda soilik erabiltzeko hobespena. Horren ordez, erabilgarri dagoen bakoitzean erabiliko da 5 GHz-ko banda."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Sare publikoaren ezarpenak aldatu dira"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Sakatu hau xehetasunak ikusteko"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-fa/strings.xml b/packages/SettingsProvider/res/values-fa/strings.xml
index 6819d2f..cc0b557 100644
--- a/packages/SettingsProvider/res/values-fa/strings.xml
+++ b/packages/SettingsProvider/res/values-fa/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"تنظیم محل ذخیره"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"تغییرات در تنظیمات نقطه اتصال"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"نوار نقطه اتصال شما تغییر کرد."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"این دستگاه از اولویت فقط ۵ گیگاهرتز شما پشتیبانی نمیکند. هرزمان نوار ۵ گیگاهرتزی دردسترس باشد، این دستگاه از آن استفاده خواهد کرد."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"تنظیمات نقطه اتصال تغییر کرده است"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"برای مشاهده جزئیات ضربه بزنید"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-fi/strings.xml b/packages/SettingsProvider/res/values-fi/strings.xml
index 9ad01eb..829bb3b 100644
--- a/packages/SettingsProvider/res/values-fi/strings.xml
+++ b/packages/SettingsProvider/res/values-fi/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Asetuksien tallennus"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Hotspot-asetustesi muutokset"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Hotspot-taajuutesi on muuttunut."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Tämä laite ei tue asetustasi (vain 5 GHz). Sen sijaan laite käyttää 5 GHz:n taajuutta sen ollessa käytettävissä."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Hotspot-asetuksia on muutettu"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Katso lisätiedot napauttamalla"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-fr-rCA/strings.xml b/packages/SettingsProvider/res/values-fr-rCA/strings.xml
index 62951bd..d640845 100644
--- a/packages/SettingsProvider/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsProvider/res/values-fr-rCA/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Stockage des paramètres"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Modifications apportées à vos paramètres de point d\'accès"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"La bande de votre point d\'accès a changé."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Cet appareil ne prend pas en charge votre préférence pour la bande de 5 GHz seulement. Au lieu de cela, cet appareil utilisera la bande de 5 GHz lorsqu\'elle sera disponible."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Les paramètres de point d\'accès ont changé"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Touchez pour afficher les renseignements"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-fr/strings.xml b/packages/SettingsProvider/res/values-fr/strings.xml
index 56bc65b..ba6ec0b 100644
--- a/packages/SettingsProvider/res/values-fr/strings.xml
+++ b/packages/SettingsProvider/res/values-fr/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Stockage des paramètres"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Modifications apportées à vos paramètres de point d\'accès"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"La bande utilisée par votre point d\'accès a été modifiée."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Cet appareil n\'est pas conçu pour utiliser exclusivement la bande 5 GHz, mais il l\'utilisera chaque fois que disponible."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Les paramètres de point d\'accès ont changé"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Appuyer ici pour plus d\'infos"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-gl/strings.xml b/packages/SettingsProvider/res/values-gl/strings.xml
index 771fade..1be162e 100644
--- a/packages/SettingsProvider/res/values-gl/strings.xml
+++ b/packages/SettingsProvider/res/values-gl/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Almacenamento da configuración"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Cambios na configuración da zona wifi"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Modificouse a banda da zona wifi."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Este dispositivo non admite a opción de conectarse só a bandas de 5 GHz, pero usaraas se están dispoñibles."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"A configuración da zona wifi cambiou"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Toca a notificación para ver os detalles"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-gu/strings.xml b/packages/SettingsProvider/res/values-gu/strings.xml
index a561924..074675f 100644
--- a/packages/SettingsProvider/res/values-gu/strings.xml
+++ b/packages/SettingsProvider/res/values-gu/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"સેટિંગ્સ સંગ્રહ"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"તમારા હૉટસ્પૉટ સેટિંગને બદલે છે"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"તમારું હૉટસ્પૉટ બૅન્ડ બદલાઈ ગયું છે."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"આ ડિવાઇસ તમારી ફક્ત 5GHz માટેની પસંદગીને સપોર્ટ કરતું નથી. તેના બદલે, જ્યારે 5GHz બૅન્ડ ઉપલબ્ધ હશે ત્યારે આ ડિવાઇસ તેનો ઉપયોગ કરશે."</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-hi/strings.xml b/packages/SettingsProvider/res/values-hi/strings.xml
index 199a546..9441a59 100644
--- a/packages/SettingsProvider/res/values-hi/strings.xml
+++ b/packages/SettingsProvider/res/values-hi/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"सेटिंग मेमोरी"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"आपकी हॉटस्पॉट की सेटिंग में किए गए बदलाव"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"आपका हॉटस्पॉट का बैंड बदल दिया गया है."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"इस डिवाइस पर आप \'सिर्फ़ 5 गीगाहर्ट्ज़\' वाला विकल्प नहीं चुन सकते. इसके बजाय, 5 गीगाहर्ट्ज़ बैंड उपलब्ध होने पर यह डिवाइस उसका इस्तेमाल करेगा."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"हॉटस्पॉट की सेटिंग बदल गई हैं"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"जानकारी देखने के लिए टैप करें"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-hr/strings.xml b/packages/SettingsProvider/res/values-hr/strings.xml
index 9129a04..206f50f 100644
--- a/packages/SettingsProvider/res/values-hr/strings.xml
+++ b/packages/SettingsProvider/res/values-hr/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Postavke pohrane"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Promjene postavki vaše žarišne točke"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Promijenila se frekvencija vaše žarišne točke."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Ovaj uređaj ne podržava vašu postavku za upotrebu samo 5 GHz. Upotrebljavat će frekvenciju od 5 GHz kada je dostupna."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Promijenile su se postavke žarišne točke"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Dodirnite da biste vidjeli pojedinosti"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-hu/strings.xml b/packages/SettingsProvider/res/values-hu/strings.xml
index a1ed494..a7ca0d2 100644
--- a/packages/SettingsProvider/res/values-hu/strings.xml
+++ b/packages/SettingsProvider/res/values-hu/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Beállítástároló"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"A hotspot beállításainak módosítása"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"A hotspot sávja megváltozott."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Ez az eszköz nem támogatja a csak 5 GHz-es sávra vonatkozó beállítást. Az eszköz akkor használ 5 GHz-es sávot, ha a sáv rendelkezésre áll."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"A hotspot beállításai módosultak"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Koppintson a részletek megtekintéséhez"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-hy/strings.xml b/packages/SettingsProvider/res/values-hy/strings.xml
index 6d716f8..4fdb880 100644
--- a/packages/SettingsProvider/res/values-hy/strings.xml
+++ b/packages/SettingsProvider/res/values-hy/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Կարգավորումների պահուստ"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Փոփոխություններ թեժ կետի կարգավորումներում"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Ձեր թեժ կետի հաճախականությունը փոխվել է։"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Սարքը չի կարող աշխատել միայն 5 ԳՀց հաճախականությամբ։ Այդ հաճախականությունը կօգտագործվի հնարավորության դեպքում։"</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Թեժ կետի կարգավորումները փոխվել են"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Հպեք՝ ավելին իմանալու համար"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-in/strings.xml b/packages/SettingsProvider/res/values-in/strings.xml
index 8b1b1f4..911892b 100644
--- a/packages/SettingsProvider/res/values-in/strings.xml
+++ b/packages/SettingsProvider/res/values-in/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Setelan Penyimpanan"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Perubahan pada setelan hotspot Anda"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Band hotspot Anda telah berubah."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Perangkat ini tidak mendukung preferensi Anda, yaitu hanya 5GHz. Sebagai gantinya, perangkat ini akan menggunakan band 5GHz jika tersedia."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Setelan hotspot telah berubah"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Ketuk untuk melihat detail"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-is/strings.xml b/packages/SettingsProvider/res/values-is/strings.xml
index 150c084..c72442e 100644
--- a/packages/SettingsProvider/res/values-is/strings.xml
+++ b/packages/SettingsProvider/res/values-is/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Stillingageymsla"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Breytingar á stillingum heits reits"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Tíðnisvið heita reitsins hefur breyst."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Þetta tæki styður ekki val þitt fyrir aðeins 5 GHz. Í staðinn mun þetta tæki nota 5 GHz tíðnisvið þegar það er í boði."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Stillingum heits reits hefur verið breytt"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Ýttu til að sjá upplýsingar"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-it/strings.xml b/packages/SettingsProvider/res/values-it/strings.xml
index 6715c3c..0e11d06 100644
--- a/packages/SettingsProvider/res/values-it/strings.xml
+++ b/packages/SettingsProvider/res/values-it/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Memoria impostazioni"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Modifiche alle tue impostazioni dell\'hotspot"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"La banda dell\'hotspot è cambiata."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Questo dispositivo non supporta la tua preferenza esclusiva per 5 GHz. Utilizzerà la banda a 5 GHz solo quando è disponibile."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Le impostazioni di hotspot sono state modificate"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Tocca per vedere i dettagli"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-iw/strings.xml b/packages/SettingsProvider/res/values-iw/strings.xml
index dd44329..8d8594d 100644
--- a/packages/SettingsProvider/res/values-iw/strings.xml
+++ b/packages/SettingsProvider/res/values-iw/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"אחסון הגדרות"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"שינויים להגדרות של הנקודה לשיתוף אינטרנט"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"התדר של הנקודה לשיתוף אינטרנט השתנה."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"מכשיר זה לא תומך בהעדפות שלך ל-5GHz בלבד. במקום זאת, מכשיר זה ישתמש בתדר 5GHz כשיהיה זמין."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"ההגדרות של הנקודה לשיתוף אינטרנט השתנו"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"יש להקיש להצגת פרטים"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-ja/strings.xml b/packages/SettingsProvider/res/values-ja/strings.xml
index 7bbcd46..5b91b2d 100644
--- a/packages/SettingsProvider/res/values-ja/strings.xml
+++ b/packages/SettingsProvider/res/values-ja/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"ストレージの設定"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"アクセス ポイントの設定の変更"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"アクセス ポイントの帯域幅が変更されました。"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"このデバイスは 5 GHz のみという設定に対応していません。ただし、5 GHz 周波数帯が利用できるときには利用します。"</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"アクセス ポイントの設定が変更されました"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"タップして詳細を確認してください"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-ka/strings.xml b/packages/SettingsProvider/res/values-ka/strings.xml
index 86db4f3..70845ac 100644
--- a/packages/SettingsProvider/res/values-ka/strings.xml
+++ b/packages/SettingsProvider/res/values-ka/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"პარამეტრების საცავი"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"თქვენი უსადენო ქსელის პარამეტრების ცვლილება"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"თქვენი უსადენო ქსელის დიაპაზონი შეიცვალა."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"ამ მოწყობილობას არ შეუძლია მხოლოდ 5 გჰც სიხშირეზე მუშაობა. აღნიშნული სიხშირის გამოყენება მოხდება მაშინ, როცა ეს შესაძლებელია."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"უსადენო ქსელის პარამეტრები შეიცვალა"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"შეეხეთ დეტალების სანახავად"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-kk/strings.xml b/packages/SettingsProvider/res/values-kk/strings.xml
index a093d08..2823e4e 100644
--- a/packages/SettingsProvider/res/values-kk/strings.xml
+++ b/packages/SettingsProvider/res/values-kk/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Параметрлер жады"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Хотспот параметрлеріне өзгерістер енгізілді"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Хотспот жолағы өзгертілді."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Бұл құрылғы тек 5 ГГц жиілікте жұмыс істей алмайды. Бұл жиілік мүмкін болған жағдайда ғана қолданылады."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Хотспот параметрлері өзгертілді"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Мәліметтерді көру үшін түртіңіз."</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-km/strings.xml b/packages/SettingsProvider/res/values-km/strings.xml
index f0a2712..cdae410 100644
--- a/packages/SettingsProvider/res/values-km/strings.xml
+++ b/packages/SettingsProvider/res/values-km/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"កំណត់ការផ្ទុក"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"ប្ដូរទៅការកំណត់ហតស្ប៉តរបស់អ្នក"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"កម្រិតបញ្ជូនហតស្ប៉តរបស់អ្នកបានផ្លាស់ប្ដូរ។"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"ឧបករណ៍នេះមិនស្គាល់ចំណូលចិត្តរបស់អ្នកសម្រាប់តែ 5GHz ប៉ុណ្ណោះ។ ផ្ទុយមកវិញ ឧបករណ៍នេះនឹងប្រើកម្រិតបញ្ជូន 5GHz នៅពេលដែលអាចប្រើបាន។"</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"បានប្ដូរការកំណត់ហតស្ប៉ត"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"ចុចដើម្បីមើលព័ត៌មានលម្អិត"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-kn/strings.xml b/packages/SettingsProvider/res/values-kn/strings.xml
index f2c1d5e..0b0000d 100644
--- a/packages/SettingsProvider/res/values-kn/strings.xml
+++ b/packages/SettingsProvider/res/values-kn/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"ಸೆಟ್ಟಿಂಗ್ಗಳ ಸಂಗ್ರಹಣೆ"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"ನಿಮ್ಮ ಹಾಟ್ಸ್ಪಾಟ್ ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿನ ಬದಲಾವಣೆಗಳು"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"ನಿಮ್ಮ ಹಾಟ್ಸ್ಪಾಟ್ ಬ್ಯಾಂಡ್ ಬದಲಾಗಿದೆ."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"ಈ ಸಾಧನವು 5GHz ಗೆ ಮಾತ್ರ ನಿಮ್ಮ ಆದ್ಯತೆಯನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ. ಬದಲಿಗೆ, ಈ ಸಾಧನವು 5GHz ಬ್ಯಾಂಡ್ ಅನ್ನು ಲಭ್ಯವಿರುವಾಗ ಬಳಸುತ್ತದೆ."</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-ko/strings.xml b/packages/SettingsProvider/res/values-ko/strings.xml
index 841832d..d76b766 100644
--- a/packages/SettingsProvider/res/values-ko/strings.xml
+++ b/packages/SettingsProvider/res/values-ko/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"설정 저장소"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"핫스팟 설정 변경"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"핫스팟 대역이 변경되었습니다."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"이 기기에서는 5GHz 전용 환경설정이 지원되지 않습니다. 대신 가능할 때만 기기에서 5GHz 대역이 사용됩니다."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"핫스팟 설정 변경됨"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"세부정보를 보려면 탭하세요."</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-ky/strings.xml b/packages/SettingsProvider/res/values-ky/strings.xml
index 014c66c..e5b82c6 100644
--- a/packages/SettingsProvider/res/values-ky/strings.xml
+++ b/packages/SettingsProvider/res/values-ky/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Жөндөөлөрдү сактоо"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Байланыш түйүнүңүздүн жөндөөлөрү өзгөрүлдү"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Байланыш түйүнүңүздүн жыштыгы өзгөрдү."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Бул түзмөк 5ГГцти гана колдонуу жөндөөсүн колдоого албайт. Анын ордуна, бул түзмөк 5ГГц жыштыгын ал жеткиликтүү болгондо колдонот."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Хотспот жөндөөлөрү өзгөрдү"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Чоо-жайын билүү үчүн басыңыз"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-lo/strings.xml b/packages/SettingsProvider/res/values-lo/strings.xml
index 9d60ba1..c2b5df7 100644
--- a/packages/SettingsProvider/res/values-lo/strings.xml
+++ b/packages/SettingsProvider/res/values-lo/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"ບ່ອນເກັບຂໍ້ມູນການຕັ້ງຄ່າ"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"ການປ່ຽນແປງການຕັ້ງຄ່າຮັອດສະປອດຂອງທ່ານ"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"ຄື້ນຄວາມຖີ່ຮັອດສະປອດຂອງທ່ານປ່ຽນແປງແລ້ວ."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"ອຸປະກອນນີ້ບໍ່ຮອງຮັບການຕັ້ງຄ່າຂອງທ່ານສຳລັບ 5GHz ເທົ່ານັ້ນ. ແຕ່ວ່າອຸປະກອນນີ້ຈະໃຊ້ຄື້ນຄວາມຖີ່ 5GHz ເມື່ອສາມາດໃຊ້ໄດ້."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"ບັນທຶກການຕັ້ງຄ່າຮັອດສະປອດແລ້ວ"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"ແຕະເພື່ອເບິ່ງລາຍລະອຽດ"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-lt/strings.xml b/packages/SettingsProvider/res/values-lt/strings.xml
index 775d3b9..64f429b 100644
--- a/packages/SettingsProvider/res/values-lt/strings.xml
+++ b/packages/SettingsProvider/res/values-lt/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Nustatymų saugykla"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Viešosios interneto prieigos taško nustatymų pakeitimai"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Viešosios prieigos taško dažnio juosta pasikeitė."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Šiame įrenginyje nepalaikoma tik 5 GHz nuostata. Vietoj to šiame įrenginyje bus naudojama 5 GHz dažnio juosta, kai bus pasiekiama."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Buvo pakeisti viešosios interneto prieigos taško nustatymai"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Palieskite ir peržiūrėkite išsamią informaciją"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-lv/strings.xml b/packages/SettingsProvider/res/values-lv/strings.xml
index f7f3117..e5af8f7 100644
--- a/packages/SettingsProvider/res/values-lv/strings.xml
+++ b/packages/SettingsProvider/res/values-lv/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Iestatījumu krātuve"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Izmaiņas tīklāja iestatījumos"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Ir mainīts tīklāja joslas platums."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Šajā ierīcē netiek atbalstīta jūsu preference par tikai 5 GHz joslu. 5 GHz josla ierīcē tiks izmantota, kad tā būs pieejama."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Ir mainīti tīklāja iestatījumi"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Pieskarieties, lai skatītu detalizētāku informāciju."</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-mk/strings.xml b/packages/SettingsProvider/res/values-mk/strings.xml
index a245e5f..13ff8a2 100644
--- a/packages/SettingsProvider/res/values-mk/strings.xml
+++ b/packages/SettingsProvider/res/values-mk/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Поставки за меморија"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Промени на поставките за точка на пристап"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Опсегот за точка на пристап е променет."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Уредов не ги поддржува вашите поставки за само 5 GHz. Наместо тоа, ќе го користи опсегот од 5 GHz кога е достапен."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Поставките за точка на пристап се променија"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Допрете за да видите детали"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-ml/strings.xml b/packages/SettingsProvider/res/values-ml/strings.xml
index b63f20e..54a05fb 100644
--- a/packages/SettingsProvider/res/values-ml/strings.xml
+++ b/packages/SettingsProvider/res/values-ml/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"സംഭരണ ക്രമീകരണം"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"നിങ്ങളുടെ ഹോട്ട്സ്പോട്ട് ക്രമീകരണത്തിൽ വരുത്തിയ മാറ്റങ്ങൾ"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"നിങ്ങളുടെ ഹോട്ട്സ്പോട്ട് ബാൻഡ് മാറിയിരിക്കുന്നു."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"നിങ്ങളുടെ മുൻഗണനയനുസരിച്ചുള്ള, 5GHz മാത്രം എന്നത് ഈ ഉപകരണം പിന്തുണയ്ക്കുന്നില്ല. പകരം, 5GHz ബാൻഡ് ലഭ്യമാകുമ്പോൾ അത് ഉപയോഗിക്കും."</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-mn/strings.xml b/packages/SettingsProvider/res/values-mn/strings.xml
index c839177..a9c2e8c 100644
--- a/packages/SettingsProvider/res/values-mn/strings.xml
+++ b/packages/SettingsProvider/res/values-mn/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Тохиргооны Сан"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Таны сүлжээний цэгийн тохиргооны өөрчлөлт"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Таны сүлжээний цэгийн хязгаарыг өөрчилсөн."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Энэ төхөөрөмж нь зөвхөн 5Гц гэсэн таны сонголтыг дэмждэггүй. Оронд нь энэ төхөөрөмж 5Гц-н хязгаарыг боломжтой үед нь ашиглах болно."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Сүлжээний цэгийн тохиргоог өөрчиллөө"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Дэлгэрэнгүй мэдээлэл харахын тулд товшино уу"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-mr/strings.xml b/packages/SettingsProvider/res/values-mr/strings.xml
index 0c7041e..0e80f70 100644
--- a/packages/SettingsProvider/res/values-mr/strings.xml
+++ b/packages/SettingsProvider/res/values-mr/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"सेटिंग्ज संचयन"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"तुमच्या हॉटस्पॉट सेटिंग्जमधील बदल"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"तुमचा हॉटस्पॉट बँड बदलला गेला आहे."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"हे डिव्हाइस फक्त ५GHz च्या तुमच्या प्राधान्याला सपोर्ट करत नाही. त्याऐवजी, उपलब्ध असेल तेव्हा हे डिव्हाइस ५GHz बँड वापरेल."</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-ms/strings.xml b/packages/SettingsProvider/res/values-ms/strings.xml
index a1574df..51a8f2b 100644
--- a/packages/SettingsProvider/res/values-ms/strings.xml
+++ b/packages/SettingsProvider/res/values-ms/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Storan Tetapan"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Perubahan kepada tetapan tempat liputan anda"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Jalur tempat liputan anda telah berubah."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Peranti ini tidak menyokong pilihan anda untuk 5GHz sahaja. Sebaliknya, peranti ini akan menggunakan jalur 5GHz apabila tersedia."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Tetapan tempat liputan telah berubah"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Ketik untuk melihat butiran"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-my/strings.xml b/packages/SettingsProvider/res/values-my/strings.xml
index 48d4dba..dc9f531 100644
--- a/packages/SettingsProvider/res/values-my/strings.xml
+++ b/packages/SettingsProvider/res/values-my/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"သိုလှောင်မှုဆက်တင်များ"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"သင်၏ဟော့စပေါ့ ဆက်တင်များ ပြောင်းလဲမှု"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"သင်၏ ဟော့စပေါ့လိုင်း ပြောင်းသွားပါပြီ။"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"ဤစက်ပစ္စည်းသည် သင်၏ 5GHz သီးသန့်ရွေးချယ်မှုအတွက် ပံ့ပိုးမထားပါ။ ၎င်းအစား ဤစက်ပစ္စည်းသည် ရနိုင်သည့်အခါ 5GHz လိုင်းကို သုံးသွားပါမည်။"</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"ဟော့စပေါ့ ဆက်တင်များ ပြောင်းသွားပြီ"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"အသေးစိတ်ကြည့်ရန် တို့ပါ"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-nb/strings.xml b/packages/SettingsProvider/res/values-nb/strings.xml
index e0cbd7e..3bdaf83 100644
--- a/packages/SettingsProvider/res/values-nb/strings.xml
+++ b/packages/SettingsProvider/res/values-nb/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Lagring av innstillinger"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Endres til innstillingene dine for Wi-Fi-soner"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Båndet ditt for Wi-Fi-sone er endret."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Denne enheten støtter ikke innstillingen din for bare 5 GHz. I stedet bruker enheten 5 GHz-båndet når det er tilgjengelig."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Innstillingene for Wi-Fi-sone er endret"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Trykk for å se detaljer"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-ne/strings.xml b/packages/SettingsProvider/res/values-ne/strings.xml
index 2fd9b00..bb04b6ba 100644
--- a/packages/SettingsProvider/res/values-ne/strings.xml
+++ b/packages/SettingsProvider/res/values-ne/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"सेटिङहरू भण्डारण"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"तपाईंका हटस्पट सेटिङहरूमा गरिएका परिवर्तनहरू"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"तपाईंको हटस्पट ब्यान्ड परिवर्तन भएको छ।"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"यो यन्त्रले तपाईंको 5GHz मात्रको प्राथमिकतालाई समर्थन गर्दैन। बरु, उपलब्ध भएको खण्डमा यो यन्त्रले 5GHz ब्यान्ड प्रयोग गर्ने छ।"</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-nl/strings.xml b/packages/SettingsProvider/res/values-nl/strings.xml
index 0b843ae..91b8542 100644
--- a/packages/SettingsProvider/res/values-nl/strings.xml
+++ b/packages/SettingsProvider/res/values-nl/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Opslagruimte voor instellingen"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Wijzigingen in je hotspot-instellingen"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Je hotspot-band is gewijzigd."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Dit apparaat biedt geen ondersteuning voor je voorkeur voor alleen 5 GHz. In plaats daarvan gebruikt dit apparaat de 5-GHz-band wanneer deze beschikbaar is."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Hotspot-instellingen zijn gewijzigd"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Tik voor meer informatie"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-or/strings.xml b/packages/SettingsProvider/res/values-or/strings.xml
index 53f6104..4b73a55 100644
--- a/packages/SettingsProvider/res/values-or/strings.xml
+++ b/packages/SettingsProvider/res/values-or/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"ସେଟିଙ୍ଗ ଷ୍ଟୋରେଜ୍"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"ଆପଣଙ୍କର ହଟ୍ସ୍ପଟ୍ ସେଟିଂସ୍କୁ ବଦଳାଇଥାଏ"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"ଆପଣଙ୍କର ହଟ୍ସ୍ପଟ୍ ପରିବର୍ତ୍ତନ କରାଯାଇଛି।"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"କେବଳ 5GHz ପାଇଁ, ଏହି ଡିଭାଇସ୍ ଆପଣଙ୍କର ପସନ୍ଦକୁ ସମର୍ଥନ କରେ ନାହିଁ। ଏହା ପରିବର୍ତ୍ତେ, ଉପଲବ୍ଧ ହେଲେ ଏହି ଡିଭାଇସ୍ 5GHz ବ୍ୟାଣ୍ଡ ବ୍ୟବହାର କରିବ।"</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-pa/strings.xml b/packages/SettingsProvider/res/values-pa/strings.xml
index 9a41e36..5af8d6a 100644
--- a/packages/SettingsProvider/res/values-pa/strings.xml
+++ b/packages/SettingsProvider/res/values-pa/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"ਸੈਟਿੰਗਾਂ ਸਟੋਰੇਜ"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"ਤੁਹਾਡੀਆਂ ਹੌਟਸਪੌਟ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਬਦਲਾਅ"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"ਤੁਹਾਡਾ ਹੌਟਸਪੌਟ ਬੈਂਡ ਬਦਲ ਗਿਆ ਹੈ।"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"ਇਹ ਡੀਵਾਈਸ ਸਿਰਫ਼ 5GHz ਦੀ ਤੁਹਾਡੀ ਤਰਜੀਹ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦਾ ਹੈ। ਇਸਦੀ ਬਜਾਏ, ਇਹ ਡੀਵਾਈਸ ਉਪਲਬਧ ਹੋਣ \'ਤੇ 5GHz ਬੈਂਡ ਵਰਤੇਗਾ।"</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-pl/strings.xml b/packages/SettingsProvider/res/values-pl/strings.xml
index 7fd2b13..d86fc4d 100644
--- a/packages/SettingsProvider/res/values-pl/strings.xml
+++ b/packages/SettingsProvider/res/values-pl/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Pamięć ustawień"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Zmieniono ustawienia hotspotu"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Zmieniono pasmo hotspotu."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"To urządzenie nie może korzystać tylko z częstotliwości 5 GHz. Będzie korzystać z tego pasma, jeśli będzie ono dostępne."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Ustawienia hotspotu zostały zmienione"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Kliknij, by zobaczyć szczegóły"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-pt-rBR/strings.xml b/packages/SettingsProvider/res/values-pt-rBR/strings.xml
index 18db8f6..a860018 100644
--- a/packages/SettingsProvider/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsProvider/res/values-pt-rBR/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Armazenamento de configurações"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Mudanças nas suas configurações de ponto de acesso"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Sua banda de ponto de acesso foi modificada."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Este dispositivo não é compatível com sua preferência apenas por 5 GHz. Em vez disso, o dispositivo usará a banda de 5 GHz quando ela estiver disponível."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"As configurações de ponto de acesso mudaram"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Toque para ver os detalhes"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-pt-rPT/strings.xml b/packages/SettingsProvider/res/values-pt-rPT/strings.xml
index be88cce..a8a8e07 100644
--- a/packages/SettingsProvider/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsProvider/res/values-pt-rPT/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Armazenamento de definições"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Alterações às definições de zona Wi-Fi"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"A banda da sua zona Wi-Fi foi alterada."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Este dispositivo não suporta a sua preferência apenas para 5 GHz. Em alternativa, este dispositivo vai utilizar a banda de 5 GHz quando estiver disponível."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"As definições da zona Wi-Fi foram alteradas"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Toque para ver os detalhes."</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-pt/strings.xml b/packages/SettingsProvider/res/values-pt/strings.xml
index 18db8f6..a860018 100644
--- a/packages/SettingsProvider/res/values-pt/strings.xml
+++ b/packages/SettingsProvider/res/values-pt/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Armazenamento de configurações"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Mudanças nas suas configurações de ponto de acesso"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Sua banda de ponto de acesso foi modificada."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Este dispositivo não é compatível com sua preferência apenas por 5 GHz. Em vez disso, o dispositivo usará a banda de 5 GHz quando ela estiver disponível."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"As configurações de ponto de acesso mudaram"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Toque para ver os detalhes"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-ro/strings.xml b/packages/SettingsProvider/res/values-ro/strings.xml
index 3a234e1..561a213 100644
--- a/packages/SettingsProvider/res/values-ro/strings.xml
+++ b/packages/SettingsProvider/res/values-ro/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Stocare setări"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Modificări aduse setărilor pentru hotspot"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"S-a schimbat banda de frecvență a hotspotului."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Dispozitivul nu acceptă doar preferința pentru 5 GHz. Dispozitivul va folosi banda de 5 GHz când este disponibilă."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Setările hotspotului s-au modificat"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Atingeți pentru detalii"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-ru/strings.xml b/packages/SettingsProvider/res/values-ru/strings.xml
index 184afdd..331fae1 100644
--- a/packages/SettingsProvider/res/values-ru/strings.xml
+++ b/packages/SettingsProvider/res/values-ru/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Хранилище настроек"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Изменения в настройках точки доступа"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Частота точки доступа изменена."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Устройство не может работать только на частоте 5 ГГц. Эта частота будет использоваться, когда это возможно."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Настройки точки доступа изменены"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Нажмите, чтобы узнать больше."</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-si/strings.xml b/packages/SettingsProvider/res/values-si/strings.xml
index 69e04f1..a9c4d0b 100644
--- a/packages/SettingsProvider/res/values-si/strings.xml
+++ b/packages/SettingsProvider/res/values-si/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"සැකසීම් ගබඩාව"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"ඔබගේ හොට්ස්පොට් සැකසීම්වලට වෙනස් කිරීම්"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"ඔබගේ හොට්ස්පොට් කලාපය වෙනස් වී ඇත."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"මෙම උපාංගය 5GHz සඳහා ඔබේ මනාපවලට සහාය නොදක්වයි. ඒ වෙනුවට, මෙම උපාංගය ලබා ගත හැකි විට 5GHz කලාපය භාවිතා කරනු ඇත."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"හොට්ස්පොට් සැකසීම් වෙනස් කර ඇත"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"විස්තර බැලීමට තට්ටු කරන්න"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-sk/strings.xml b/packages/SettingsProvider/res/values-sk/strings.xml
index a53178d..5712d05 100644
--- a/packages/SettingsProvider/res/values-sk/strings.xml
+++ b/packages/SettingsProvider/res/values-sk/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Ukladací priestor nastavení"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Zmeny nastavení hotspotu"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Pásmo vášho hotspotu sa zmenilo."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Toto zariadenie nepodporuje vašu predvoľbu používať iba 5 GHz. Namiesto toho bude pásmo 5 GHz používať vtedy, keď bude k dispozícii."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Nastavenia hotspotu boli zmenené"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Klepnutím zobrazíte podrobnosti"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-sl/strings.xml b/packages/SettingsProvider/res/values-sl/strings.xml
index ea697fe..4e265fb 100644
--- a/packages/SettingsProvider/res/values-sl/strings.xml
+++ b/packages/SettingsProvider/res/values-sl/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Shramba nastavitev"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Spremembe nastavitev dostopne točke"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Pas dostopne točke je spremenjen."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Ta naprava ne podpira prednostne nastavitve samo za 5-GHz pas. Namesto tega bo ta naprava uporabljala 5-GHz pas, ko bo na voljo."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Nastavitve dostopne ročke so spremenjene"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Dotaknite se za ogled podrobnosti"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-sq/strings.xml b/packages/SettingsProvider/res/values-sq/strings.xml
index a111576..8bbe2e7 100644
--- a/packages/SettingsProvider/res/values-sq/strings.xml
+++ b/packages/SettingsProvider/res/values-sq/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Hapësira ruajtëse e \"Cilësimeve\""</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Ndryshimet në cilësimet e zonës së qasjes për internet"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Brezi yt i zonës së qasjes për internet ka ndryshuar."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Kjo pajisje nuk e mbështet preferencën për vetëm 5 GHz. Përkundrazi, pajisja do të përdorë brezin 5 GHz nëse ka."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Cilësimet e zonës së qasjes për internet kanë ndryshuar"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Trokit për të parë detajet"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-sr/strings.xml b/packages/SettingsProvider/res/values-sr/strings.xml
index d473102..4d05762 100644
--- a/packages/SettingsProvider/res/values-sr/strings.xml
+++ b/packages/SettingsProvider/res/values-sr/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Подешавања складишта"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Промене подешавања за хотспот"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Опсег хотспота је промењен."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Овај уређај не подржава подешавање само за 5 GHz. Уређај ће користити опсег од 5 GHz када буде доступан."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Подешавања хотспота су промењена"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Додирните да бисте видели детаље"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-sv/strings.xml b/packages/SettingsProvider/res/values-sv/strings.xml
index fea3e5e..5ee4703 100644
--- a/packages/SettingsProvider/res/values-sv/strings.xml
+++ b/packages/SettingsProvider/res/values-sv/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Lagring av inställningar"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Ändringar i inställningarna för surfzon"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Frekvensbandet för surfzonen har ändrats."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Den här enheten har inte stöd för inställningen för att endast använda 5 GHz. I stället används 5 GHz när det är möjligt."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Inställningarna för surfzon har ändrats"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Tryck här för mer information"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-sw/strings.xml b/packages/SettingsProvider/res/values-sw/strings.xml
index 4d05817..59f82a9 100644
--- a/packages/SettingsProvider/res/values-sw/strings.xml
+++ b/packages/SettingsProvider/res/values-sw/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Hifadhi ya Mipangilio"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Mabadiliko kwenye mipangilio ya mtandaopepe"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Bendi ya mtandaopepe wako imebadilika."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Kifaa hiki hakitumii mapendeleo yako ya GHz 5 pekee. Badala yake, kifaa hiki kitatumia bendi ya GHz 5 itakapopatikana."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Mipangilio ya mtandaopepe imebadilika"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Gusa ili uangalie maelezo"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-ta/strings.xml b/packages/SettingsProvider/res/values-ta/strings.xml
index f518a78..fa6b8cd 100644
--- a/packages/SettingsProvider/res/values-ta/strings.xml
+++ b/packages/SettingsProvider/res/values-ta/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"அமைப்புகளின் சேமிப்பிடம்"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"உங்கள் ஹாட்ஸ்பாட் அமைப்புகளில் செய்யப்பட்டுள்ள மாற்றங்கள்"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"உங்கள் ஹாட்ஸ்பாட்டின் அலைவரிசை வரம்பு மாறிவிட்டது."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"இந்தச் சாதனத்தில், ’5GHz மட்டும்’ எனும் முன்னுரிமைத் தேர்வு ஆதரிக்கப்படவில்லை. எனினும் 5GHz அலைவரிசை வரம்பிற்குள் இருக்கும்போது சாதனம் அதைப் பயன்படுத்திக்கொள்ளும்."</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-te/strings.xml b/packages/SettingsProvider/res/values-te/strings.xml
index 6c59223..b1955ed 100644
--- a/packages/SettingsProvider/res/values-te/strings.xml
+++ b/packages/SettingsProvider/res/values-te/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"సెట్టింగ్ల నిల్వ"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"మీ హాట్స్పాట్ సెట్టింగ్లకు మార్పులు"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"మీ హాట్స్పాట్ బ్యాండ్ మార్చబడింది."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"ఈ పరికరం 5GHz కోసం మాత్రమే మీ ప్రాధాన్యతకు మద్దతు ఇవ్వదు. బదులుగా, ఈ పరికరం అందుబాటులో ఉన్నప్పుడు 5GHz బ్యాండ్ను ఉపయోగిస్తుంది."</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-th/strings.xml b/packages/SettingsProvider/res/values-th/strings.xml
index 4bf148f..ed63174 100644
--- a/packages/SettingsProvider/res/values-th/strings.xml
+++ b/packages/SettingsProvider/res/values-th/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"ที่เก็บข้อมูลการตั้งค่า"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"มีการเปลี่ยนแปลงการตั้งค่าฮอตสปอต"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"ย่านความถี่ฮอตสปอตมีการเปลี่ยนแปลง"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"อุปกรณ์นี้ไม่รองรับค่ากำหนดของคุณเฉพาะสำหรับ 5 GHz เท่านั้น และจะใช้ย่านความถี่ 5 GHz แทน เมื่อใช้ได้"</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"การตั้งค่าฮอตสปอตมีการเปลี่ยนแปลง"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"แตะเพื่อดูรายละเอียด"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-tl/strings.xml b/packages/SettingsProvider/res/values-tl/strings.xml
index 2a36d58..3d6be40 100644
--- a/packages/SettingsProvider/res/values-tl/strings.xml
+++ b/packages/SettingsProvider/res/values-tl/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Storage ng Mga Setting"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Mga pagbabago sa mga setting ng iyong hotspot"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Nagbago ang band ng iyong hotspot."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Hindi sinusuportahan ng device na ito ang kagustuhan mong gumamit lang ng 5GHz. Sa halip, gagamitin ng device na ito ang 5GHz na band kapag available."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Nabago ang mga setting ng hotspot"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"I-tap para makita ang mga detalye"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-tr/strings.xml b/packages/SettingsProvider/res/values-tr/strings.xml
index add1fdb..75e908f 100644
--- a/packages/SettingsProvider/res/values-tr/strings.xml
+++ b/packages/SettingsProvider/res/values-tr/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Ayarlar Deposu"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Hotspot ayarlarınız değişti"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Hotspot bandınız değişti."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Bu cihaz yalnızca 5 GHz bandının kullanılmasına yönelik tercihinizi desteklemiyor. Bunun yerine, bu cihaz 5 GHz bandını mevcut olduğunda kullanacak."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Hotspot ayarları değişti"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Ayrıntıları görmek için dokunun"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-uk/strings.xml b/packages/SettingsProvider/res/values-uk/strings.xml
index cd678bc..2dbb360 100644
--- a/packages/SettingsProvider/res/values-uk/strings.xml
+++ b/packages/SettingsProvider/res/values-uk/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Сховище налаштувань"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Зміни в налаштуваннях точки доступу"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Діапазон частот точки доступу змінено."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"На цьому пристрої не підтримується налаштування \"Лише 5 ГГц\". Натомість буде використано діапазон частот 5 ГГц (якщо доступно)."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Налаштування точки доступу змінились"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Торкніться, щоб переглянути докладні відомості"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-ur/strings.xml b/packages/SettingsProvider/res/values-ur/strings.xml
index 2241ce9..2ce44b1 100644
--- a/packages/SettingsProvider/res/values-ur/strings.xml
+++ b/packages/SettingsProvider/res/values-ur/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"ترتیبات کا اسٹوریج"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"اپنے ہاٹ اسپاٹ کی ترتیبات میں تبدیلیاں"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"آپ کا ہاٹ اسپات بینڈ تبدیل ہو گیا ہے۔"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"یہ آلہ صرف 5GHz کے لیے آپ کی ترجیح کو سپورٹ نہیں کرے گا۔ بلکہ 5GHz بینڈ کے دستیاب ہونے پر اس کا استعمال کرے گا۔"</string>
+ <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
+ <skip />
+ <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
+ <skip />
</resources>
diff --git a/packages/SettingsProvider/res/values-uz/strings.xml b/packages/SettingsProvider/res/values-uz/strings.xml
index a266bf0..bb6e22e 100644
--- a/packages/SettingsProvider/res/values-uz/strings.xml
+++ b/packages/SettingsProvider/res/values-uz/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Sozlamalar xotirasi"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Hotspot sozlamalari o‘zgartirildi"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Hotspot chastotasi oʻzgartirildi."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Qurilma faqat 5 GGs chastotada ishlay olmaydi. Bu chastotadan imkoniyatga qarab foydalaniladi."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Hotspot sozlamalari oʻzgardi"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Tafsilotlar uchun bosing"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-vi/strings.xml b/packages/SettingsProvider/res/values-vi/strings.xml
index 74f93b2..4608983 100644
--- a/packages/SettingsProvider/res/values-vi/strings.xml
+++ b/packages/SettingsProvider/res/values-vi/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Lưu trữ bộ nhớ"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Những thay đổi trong mục cài đặt điểm phát sóng của bạn"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Bằng tần của điểm phát sóng đã thay đổi."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Thiết bị này không hỗ trợ tùy chọn chỉ sử dụng băng tần 5 GHz. Thay vào đó, thiết bị này sẽ sử dụng băng tần 5 GHz khi có thể."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Đã thay đổi các tùy chọn cài đặt điểm phát sóng"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Nhấn để xem chi tiết"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-zh-rCN/strings.xml b/packages/SettingsProvider/res/values-zh-rCN/strings.xml
index 95b15e0..a08afc8 100644
--- a/packages/SettingsProvider/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsProvider/res/values-zh-rCN/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"设置存储"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"您的热点设置已变更"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"您的热点频段已变更。"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"此设备不支持您的偏好设置(仅限 5GHz),而且会在 5GHz 频段可用时使用该频段。"</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"热点设置已更改"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"点按即可查看详细信息"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-zh-rHK/strings.xml b/packages/SettingsProvider/res/values-zh-rHK/strings.xml
index 41ebe27..fb91dbb 100644
--- a/packages/SettingsProvider/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsProvider/res/values-zh-rHK/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"設定儲存空間"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"您的熱點設定變更"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"您的熱點頻段已變更。"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"此裝置不支援只限 5 GHz 的偏好設定,但會在 5 GHz 頻段可用時採用。"</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"熱點設定已變更"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"輕按以查看詳情"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-zh-rTW/strings.xml b/packages/SettingsProvider/res/values-zh-rTW/strings.xml
index d0a30f5..1b8fcb2 100644
--- a/packages/SettingsProvider/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsProvider/res/values-zh-rTW/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"設定儲存空間"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"無線基地台設定變更"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"你的無線基地台頻帶已變更。"</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"這部裝置不支援你的偏好設定 (僅限 5GHz),而是會在 5GHz 可用時使用該頻帶。"</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"無線基地台設定有所變更"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"輕觸即可查看詳細資料"</string>
</resources>
diff --git a/packages/SettingsProvider/res/values-zu/strings.xml b/packages/SettingsProvider/res/values-zu/strings.xml
index 0440b3b..dad24b4 100644
--- a/packages/SettingsProvider/res/values-zu/strings.xml
+++ b/packages/SettingsProvider/res/values-zu/strings.xml
@@ -20,7 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Izilungiselelo zesitoreji"</string>
- <string name="wifi_softap_config_change" msgid="5338670993556993667">"Ushintsho kuzilungiselelo zakho ze-hotspot"</string>
- <string name="wifi_softap_config_change_summary" msgid="7600005249167787750">"Ibhendi yakho ye-hotspot ishintshile."</string>
- <string name="wifi_softap_config_change_detailed" msgid="2504664754843959730">"Le divayisi ayisekeli okuncamelayo kwe-5GHz kuphela. Kunalokho, le divayisi izosebenzisa ibhendi ye-5GHz uma itholakala."</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Izilungiselelo ze-Hotspot zishintshile"</string>
+ <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Thepha ukuze ubone imininingwane"</string>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index e80b437..7dcf4b0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -24,6 +24,7 @@
import android.os.Message;
import android.os.Trace;
import android.view.Surface;
+import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
@@ -39,7 +40,7 @@
private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
- private final Surface mTargetSurface;
+ private final SurfaceControl mBarrierSurfaceControl;
private final ViewRootImpl mTargetViewRootImpl;
private final Handler mApplyHandler;
@@ -52,7 +53,8 @@
*/
public SyncRtSurfaceTransactionApplierCompat(View targetView) {
mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
- mTargetSurface = mTargetViewRootImpl != null ? mTargetViewRootImpl.mSurface : null;
+ mBarrierSurfaceControl = mTargetViewRootImpl != null
+ ? mTargetViewRootImpl.getRenderSurfaceControl() : null;
mApplyHandler = new Handler(new Callback() {
@Override
@@ -91,7 +93,7 @@
mTargetViewRootImpl.registerRtFrameCallback(new HardwareRenderer.FrameDrawingCallback() {
@Override
public void onFrameDraw(long frame) {
- if (mTargetSurface == null || !mTargetSurface.isValid()) {
+ if (mBarrierSurfaceControl == null || !mBarrierSurfaceControl.isValid()) {
Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
.sendToTarget();
return;
@@ -102,7 +104,7 @@
SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams =
params[i];
SurfaceControlCompat surface = surfaceParams.surface;
- t.deferTransactionUntil(surface, mTargetSurface, frame);
+ t.deferTransactionUntil(surface, mBarrierSurfaceControl, frame);
applyParams(t, surfaceParams);
}
t.setEarlyWakeup();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index 073688b..8f4926f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -20,6 +20,7 @@
import android.graphics.Rect;
import android.view.Surface;
import android.view.SurfaceControl.Transaction;
+import android.view.SurfaceControl;
public class TransactionCompat {
@@ -87,8 +88,8 @@
}
public TransactionCompat deferTransactionUntil(SurfaceControlCompat surfaceControl,
- Surface barrier, long frameNumber) {
- mTransaction.deferTransactionUntilSurface(surfaceControl.mSurfaceControl, barrier,
+ SurfaceControl barrier, long frameNumber) {
+ mTransaction.deferTransactionUntil(surfaceControl.mSurfaceControl, barrier,
frameNumber);
return this;
}
@@ -102,4 +103,4 @@
mTransaction.setColor(surfaceControl.mSurfaceControl, color);
return this;
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e5f6d3c..9ba3860 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -95,6 +95,7 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.util.Assert;
import com.google.android.collect.Lists;
@@ -341,7 +342,7 @@
@Override
public void onTrustChanged(boolean enabled, int userId, int flags) {
- checkIsHandlerThread();
+ Assert.isMainThread();
mUserHasTrust.put(userId, enabled);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -360,7 +361,7 @@
}
private void handleSimSubscriptionInfoChanged() {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (DEBUG_SIM_STATES) {
Log.v(TAG, "onSubscriptionInfoChanged()");
List<SubscriptionInfo> sil = mSubscriptionManager
@@ -403,7 +404,7 @@
}
private void callbacksRefreshCarrierInfo() {
- checkIsHandlerThread();
+ Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -466,7 +467,7 @@
@Override
public void onTrustManagedChanged(boolean managed, int userId) {
- checkIsHandlerThread();
+ Assert.isMainThread();
mUserTrustIsManaged.put(userId, managed);
mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId));
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -523,7 +524,7 @@
@VisibleForTesting
protected void onFingerprintAuthenticated(int userId) {
- checkIsHandlerThread();
+ Assert.isMainThread();
Trace.beginSection("KeyGuardUpdateMonitor#onFingerPrintAuthenticated");
mUserFingerprintAuthenticated.put(userId, true);
// Update/refresh trust state only if user can skip bouncer
@@ -549,7 +550,7 @@
}
private void handleFingerprintAuthFailed() {
- checkIsHandlerThread();
+ Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -561,7 +562,7 @@
}
private void handleFingerprintAcquired(int acquireInfo) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (acquireInfo != FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
return;
}
@@ -599,7 +600,7 @@
}
private void handleFingerprintHelp(int msgId, String helpString) {
- checkIsHandlerThread();
+ Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -618,7 +619,7 @@
};
private void handleFingerprintError(int msgId, String errString) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED && mHandler.hasCallbacks(
mCancelNotReceived)) {
mHandler.removeCallbacks(mCancelNotReceived);
@@ -671,7 +672,7 @@
}
private void notifyFingerprintRunningStateChanged() {
- checkIsHandlerThread();
+ Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -684,7 +685,7 @@
@VisibleForTesting
protected void onFaceAuthenticated(int userId) {
Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated");
- checkIsHandlerThread();
+ Assert.isMainThread();
mUserFaceAuthenticated.put(userId, true);
// Update/refresh trust state only if user can skip bouncer
if (getUserCanSkipBouncer(userId)) {
@@ -710,7 +711,7 @@
}
private void handleFaceAuthFailed() {
- checkIsHandlerThread();
+ Assert.isMainThread();
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -723,7 +724,7 @@
}
private void handleFaceAcquired(int acquireInfo) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (acquireInfo != FaceManager.FACE_ACQUIRED_GOOD) {
return;
}
@@ -767,7 +768,7 @@
}
private void handleFaceHelp(int msgId, String helpString) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (DEBUG_FACE) Log.d(TAG, "Face help received: " + helpString);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -787,7 +788,7 @@
};
private void handleFaceError(int msgId, String errString) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (DEBUG_FACE) Log.d(TAG, "Face error received: " + errString);
if (msgId == FaceManager.FACE_ERROR_CANCELED && mHandler.hasCallbacks(mCancelNotReceived)) {
mHandler.removeCallbacks(mCancelNotReceived);
@@ -842,7 +843,7 @@
}
private void notifyFaceRunningStateChanged() {
- checkIsHandlerThread();
+ Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -853,7 +854,7 @@
}
private void handleFaceUnlockStateChanged(boolean running, int userId) {
- checkIsHandlerThread();
+ Assert.isMainThread();
mUserFaceUnlockRunning.put(userId, running);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -965,7 +966,7 @@
* Cached version of {@link TrustManager#isTrustUsuallyManaged(int)}.
*/
public boolean isTrustUsuallyManaged(int userId) {
- checkIsHandlerThread();
+ Assert.isMainThread();
return mUserTrustIsUsuallyManaged.get(userId);
}
@@ -996,7 +997,7 @@
}
private void notifyStrongAuthStateChanged(int userId) {
- checkIsHandlerThread();
+ Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1010,7 +1011,7 @@
}
private void dispatchErrorMessage(CharSequence message) {
- checkIsHandlerThread();
+ Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1323,7 +1324,7 @@
protected void handleStartedWakingUp() {
Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
- checkIsHandlerThread();
+ Assert.isMainThread();
updateBiometricListeningState();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1335,7 +1336,7 @@
}
protected void handleStartedGoingToSleep(int arg1) {
- checkIsHandlerThread();
+ Assert.isMainThread();
mLockIconPressed = false;
clearBiometricRecognized();
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1349,7 +1350,7 @@
}
protected void handleFinishedGoingToSleep(int arg1) {
- checkIsHandlerThread();
+ Assert.isMainThread();
mGoingToSleep = false;
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1361,7 +1362,7 @@
}
private void handleScreenTurnedOn() {
- checkIsHandlerThread();
+ Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1373,7 +1374,7 @@
private void handleScreenTurnedOff() {
final String tag = "KeyguardUpdateMonitor#handleScreenTurnedOff";
DejankUtils.startDetectingBlockingIpcs(tag);
- checkIsHandlerThread();
+ Assert.isMainThread();
mHardwareFingerprintUnavailableRetryCount = 0;
mHardwareFaceUnavailableRetryCount = 0;
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1386,7 +1387,7 @@
}
private void handleDreamingStateChanged(int dreamStart) {
- checkIsHandlerThread();
+ Assert.isMainThread();
mIsDreaming = dreamStart == 1;
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1398,7 +1399,7 @@
}
private void handleUserInfoChanged(int userId) {
- checkIsHandlerThread();
+ Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1408,7 +1409,7 @@
}
private void handleUserUnlocked(int userId) {
- checkIsHandlerThread();
+ Assert.isMainThread();
mUserIsUnlocked.put(userId, true);
mNeedsSlowUnlockTransition = resolveNeedsSlowUnlockTransition();
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1420,20 +1421,22 @@
}
private void handleUserStopped(int userId) {
- checkIsHandlerThread();
+ Assert.isMainThread();
mUserIsUnlocked.put(userId, mUserManager.isUserUnlocked(userId));
}
@VisibleForTesting
void handleUserRemoved(int userId) {
- checkIsHandlerThread();
+ Assert.isMainThread();
mUserIsUnlocked.delete(userId);
mUserTrustIsUsuallyManaged.delete(userId);
}
@VisibleForTesting
@Inject
- protected KeyguardUpdateMonitor(Context context, @Main Looper mainLooper,
+ protected KeyguardUpdateMonitor(
+ Context context,
+ @Main Looper mainLooper,
BroadcastDispatcher broadcastDispatcher,
DumpController dumpController) {
mContext = context;
@@ -1962,7 +1965,7 @@
* @param hasLockscreenWallpaper Whether Keyguard has a lockscreen wallpaper.
*/
public void setHasLockscreenWallpaper(boolean hasLockscreenWallpaper) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (hasLockscreenWallpaper != mHasLockscreenWallpaper) {
mHasLockscreenWallpaper = hasLockscreenWallpaper;
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1985,7 +1988,7 @@
* Handle {@link #MSG_DPM_STATE_CHANGED}
*/
private void handleDevicePolicyManagerStateChanged(int userId) {
- checkIsHandlerThread();
+ Assert.isMainThread();
updateFingerprintListeningState();
updateSecondaryLockscreenRequirement(userId);
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -2000,7 +2003,7 @@
* Handle {@link #MSG_USER_SWITCHING}
*/
private void handleUserSwitching(int userId, IRemoteCallback reply) {
- checkIsHandlerThread();
+ Assert.isMainThread();
mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId));
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -2018,7 +2021,7 @@
* Handle {@link #MSG_USER_SWITCH_COMPLETE}
*/
private void handleUserSwitchComplete(int userId) {
- checkIsHandlerThread();
+ Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -2031,7 +2034,7 @@
* Handle {@link #MSG_DEVICE_PROVISIONED}
*/
private void handleDeviceProvisioned() {
- checkIsHandlerThread();
+ Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -2049,7 +2052,7 @@
* Handle {@link #MSG_PHONE_STATE_CHANGED}
*/
private void handlePhoneStateChanged(String newState) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (DEBUG) Log.d(TAG, "handlePhoneStateChanged(" + newState + ")");
if (TelephonyManager.EXTRA_STATE_IDLE.equals(newState)) {
mPhoneState = TelephonyManager.CALL_STATE_IDLE;
@@ -2070,7 +2073,7 @@
* Handle {@link #MSG_RINGER_MODE_CHANGED}
*/
private void handleRingerModeChange(int mode) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (DEBUG) Log.d(TAG, "handleRingerModeChange(" + mode + ")");
mRingMode = mode;
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -2085,7 +2088,7 @@
* Handle {@link #MSG_TIME_UPDATE}
*/
private void handleTimeUpdate() {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (DEBUG) Log.d(TAG, "handleTimeUpdate");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -2099,7 +2102,7 @@
* Handle (@line #MSG_TIMEZONE_UPDATE}
*/
private void handleTimeZoneUpdate(String timeZone) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (DEBUG) Log.d(TAG, "handleTimeZoneUpdate");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -2115,7 +2118,7 @@
* Handle {@link #MSG_BATTERY_UPDATE}
*/
private void handleBatteryUpdate(BatteryStatus status) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (DEBUG) Log.d(TAG, "handleBatteryUpdate");
final boolean batteryUpdateInteresting = isBatteryUpdateInteresting(mBatteryStatus, status);
mBatteryStatus = status;
@@ -2134,7 +2137,7 @@
*/
@VisibleForTesting
void updateTelephonyCapable(boolean capable) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (capable == mTelephonyCapable) {
return;
}
@@ -2152,7 +2155,7 @@
*/
@VisibleForTesting
void handleSimStateChange(int subId, int slotId, int state) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (DEBUG_SIM_STATES) {
Log.d(TAG, "handleSimStateChange(subId=" + subId + ", slotId="
+ slotId + ", state=" + state + ")");
@@ -2236,7 +2239,7 @@
* <p>Needs to be called from the main thread.
*/
public void onKeyguardVisibilityChanged(boolean showing) {
- checkIsHandlerThread();
+ Assert.isMainThread();
Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")");
mKeyguardIsVisible = showing;
@@ -2279,7 +2282,7 @@
* @see #sendKeyguardBouncerChanged(boolean)
*/
private void handleKeyguardBouncerChanged(int bouncer) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (DEBUG) Log.d(TAG, "handleKeyguardBouncerChanged(" + bouncer + ")");
boolean isBouncer = (bouncer == 1);
mBouncer = isBouncer;
@@ -2305,7 +2308,7 @@
* Handle {@link #MSG_REPORT_EMERGENCY_CALL_ACTION}
*/
private void handleReportEmergencyCallAction() {
- checkIsHandlerThread();
+ Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -2344,7 +2347,7 @@
* @param callback The callback to remove
*/
public void removeCallback(KeyguardUpdateMonitorCallback callback) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (DEBUG) {
Log.v(TAG, "*** unregister callback for " + callback);
}
@@ -2359,7 +2362,7 @@
* @param callback The callback to register
*/
public void registerCallback(KeyguardUpdateMonitorCallback callback) {
- checkIsHandlerThread();
+ Assert.isMainThread();
if (DEBUG) Log.v(TAG, "*** register callback for " + callback);
// Prevent adding duplicate callbacks
@@ -2448,7 +2451,7 @@
if (!bypassHandler) {
mHandler.obtainMessage(MSG_REPORT_EMERGENCY_CALL_ACTION).sendToTarget();
} else {
- checkIsHandlerThread();
+ Assert.isMainThread();
handleReportEmergencyCallAction();
}
}
@@ -2466,7 +2469,7 @@
}
public void clearBiometricRecognized() {
- checkIsHandlerThread();
+ Assert.isMainThread();
mUserFingerprintAuthenticated.clear();
mUserFaceAuthenticated.clear();
mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT);
@@ -2653,7 +2656,7 @@
}
private void updateLogoutEnabled() {
- checkIsHandlerThread();
+ Assert.isMainThread();
boolean logoutEnabled = mDevicePolicyManager.isLogoutEnabled();
if (mLogoutEnabled != logoutEnabled) {
mLogoutEnabled = logoutEnabled;
@@ -2667,13 +2670,6 @@
}
}
- private void checkIsHandlerThread() {
- if (!mHandler.getLooper().isCurrentThread()) {
- Log.wtfStack(TAG, "must call on mHandler's thread "
- + mHandler.getLooper().getThread() + ", not " + Thread.currentThread());
- }
- }
-
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("KeyguardUpdateMonitor state:");
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 3d9865c..955edcf 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -648,7 +648,7 @@
String appName = topBubble.getAppName();
Notification notification = topBubble.getEntry().getSbn().getNotification();
CharSequence titleCharSeq = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
- String titleStr = getResources().getString(R.string.stream_notification);
+ String titleStr = getResources().getString(R.string.notification_bubble_title);
if (titleCharSeq != null) {
titleStr = titleCharSeq.toString();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index a57ec5b..3e257b6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -38,8 +38,10 @@
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.ShadeControllerImpl;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -92,10 +94,14 @@
@Singleton
@Provides
- static HeadsUpManagerPhone provideHeadsUpManagerPhone(Context context,
+ static HeadsUpManagerPhone provideHeadsUpManagerPhone(
+ Context context,
StatusBarStateController statusBarStateController,
- KeyguardBypassController bypassController) {
- return new HeadsUpManagerPhone(context, statusBarStateController, bypassController);
+ KeyguardBypassController bypassController,
+ NotificationGroupManager groupManager,
+ ConfigurationController configurationController) {
+ return new HeadsUpManagerPhone(context, statusBarStateController, bypassController,
+ groupManager, configurationController);
}
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 0c86157..1ab77f3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -17,9 +17,9 @@
package com.android.systemui.qs.tiles;
import android.annotation.Nullable;
-import android.content.ComponentName;
import android.content.Intent;
import android.os.UserManager;
+import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.util.Log;
import android.widget.Switch;
@@ -36,9 +36,6 @@
/** Quick settings tile: Hotspot **/
public class HotspotTile extends QSTileImpl<BooleanState> {
- private static final Intent TETHER_SETTINGS = new Intent().setComponent(new ComponentName(
- "com.android.settings", "com.android.settings.TetherSettings"));
-
private final Icon mEnabledStatic = ResourceIcon.get(R.drawable.ic_hotspot);
private final HotspotController mHotspotController;
@@ -79,7 +76,7 @@
@Override
public Intent getLongClickIntent() {
- return new Intent(TETHER_SETTINGS);
+ return new Intent(Settings.ACTION_TETHER_SETTINGS);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index 72a7e11..07cf9d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -53,12 +53,14 @@
VisualStabilityManager visualStabilityManager,
StatusBarStateController statusBarStateController,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
- NotificationListener notificationListener) {
+ NotificationListener notificationListener,
+ HeadsUpManager headsUpManager) {
mRemoteInputManager = remoteInputManager;
mVisualStabilityManager = visualStabilityManager;
mStatusBarStateController = statusBarStateController;
mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
mNotificationListener = notificationListener;
+ mHeadsUpManager = headsUpManager;
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
@@ -81,10 +83,6 @@
});
}
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
/**
* Adds the entry to the respective alerting manager if the content view was inflated and
* the entry should still alert.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 4f55e02..5ebd368 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -49,9 +49,7 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.Assert;
import com.android.systemui.util.leak.LeakDetector;
@@ -230,9 +228,7 @@
mRemoveInterceptors.remove(interceptor);
}
- public void setUpWithPresenter(NotificationPresenter presenter,
- NotificationListContainer listContainer,
- HeadsUpManager headsUpManager) {
+ public void setUpWithPresenter(NotificationPresenter presenter) {
mPresenter = presenter;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 616c110..fabe3a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -61,7 +61,8 @@
* Injected constructor. See {@link NotificationsModule}.
*/
public VisualStabilityManager(
- NotificationEntryManager notificationEntryManager, @Main Handler handler) {
+ NotificationEntryManager notificationEntryManager,
+ @Main Handler handler) {
mHandler = handler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 8f8f742..d0b553d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -47,6 +47,7 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
import java.util.concurrent.Executor;
@@ -123,14 +124,16 @@
VisualStabilityManager visualStabilityManager,
StatusBarStateController statusBarStateController,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
- NotificationListener notificationListener) {
+ NotificationListener notificationListener,
+ HeadsUpManager headsUpManager) {
return new NotificationAlertingManager(
notificationEntryManager,
remoteInputManager,
visualStabilityManager,
statusBarStateController,
notificationInterruptionStateProvider,
- notificationListener);
+ notificationListener,
+ headsUpManager);
}
/** Provides an instance of {@link NotificationLogger} */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 3eac229..b03ba3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -336,6 +336,10 @@
return false;
}
+ boolean superPerformClick() {
+ return super.performClick();
+ }
+
/**
* Cancels the hotspot and makes the notification inactive.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index 8465658..2643ec9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -72,7 +72,8 @@
} else {
mView.makeInactive(true /* animate */);
}
- }, mView::performClick, mView::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
+ }, mView::superPerformClick, mView::handleSlideBack,
+ mFalsingManager::onNotificationDoubleTap);
mView.setOnTouchListener(mTouchHandler);
mView.setTouchHandler(mTouchHandler);
mView.setOnDimmedListener(dimmed -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index c6e3fde..63fe700 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -19,24 +19,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Rect;
import android.graphics.Region;
-import android.util.Log;
import android.util.Pools;
-import android.view.DisplayCutout;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.WindowInsets;
import androidx.collection.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
-import com.android.systemui.ScreenDecorations;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.StatusBarState;
@@ -49,31 +40,27 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.Stack;
/**
* A implementation of HeadsUpManager for phone and car.
*/
public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
- VisualStabilityManager.Callback, OnHeadsUpChangedListener,
- ConfigurationController.ConfigurationListener, StateListener {
+ VisualStabilityManager.Callback, OnHeadsUpChangedListener {
private static final String TAG = "HeadsUpManagerPhone";
@VisibleForTesting
final int mExtensionTime;
- private final StatusBarStateController mStatusBarStateController;
private final KeyguardBypassController mBypassController;
+ private final NotificationGroupManager mGroupManager;
+ private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>();
private final int mAutoHeadsUpNotificationDecay;
- private View mNotificationShadeWindowView;
- private NotificationGroupManager mGroupManager;
private VisualStabilityManager mVisualStabilityManager;
- private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
private boolean mReleaseOnExpandFinish;
- private int mStatusBarHeight;
- private int mHeadsUpInset;
- private int mDisplayCutoutTouchableRegionSize;
private boolean mTrackingHeadsUp;
private HashSet<String> mSwipedOutKeys = new HashSet<>();
private HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
@@ -81,12 +68,13 @@
private ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
= new ArraySet<>();
private boolean mIsExpanded;
- private int[] mTmpTwoArray = new int[2];
private boolean mHeadsUpGoingAway;
private int mStatusBarState;
- private Region mTouchableRegion = new Region();
-
private AnimationStateHandler mAnimationStateHandler;
+ private int mHeadsUpInset;
+
+ // Used for determining the region for touch interaction
+ private final Region mTouchableRegion = new Region();
private final Pools.Pool<HeadsUpEntryPhone> mEntryPool = new Pools.Pool<HeadsUpEntryPhone>() {
private Stack<HeadsUpEntryPhone> mPoolObjects = new Stack<>();
@@ -111,76 +99,96 @@
public HeadsUpManagerPhone(@NonNull final Context context,
StatusBarStateController statusBarStateController,
- KeyguardBypassController bypassController) {
+ KeyguardBypassController bypassController,
+ NotificationGroupManager groupManager,
+ ConfigurationController configurationController) {
super(context);
Resources resources = mContext.getResources();
mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
mAutoHeadsUpNotificationDecay = resources.getInteger(
R.integer.auto_heads_up_notification_decay);
- mStatusBarStateController = statusBarStateController;
- mStatusBarStateController.addCallback(this);
+ statusBarStateController.addCallback(mStatusBarStateListener);
mBypassController = bypassController;
-
- initResources();
- }
-
- public void setUp(@NonNull View notificationShadeWindowView,
- @NonNull NotificationGroupManager groupManager,
- @NonNull StatusBar bar,
- @NonNull VisualStabilityManager visualStabilityManager) {
- mNotificationShadeWindowView = notificationShadeWindowView;
- mStatusBarTouchableRegionManager = new StatusBarTouchableRegionManager(this, bar,
- notificationShadeWindowView);
mGroupManager = groupManager;
- mVisualStabilityManager = visualStabilityManager;
- addListener(new OnHeadsUpChangedListener() {
+ updateResources();
+ configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
@Override
- public void onHeadsUpPinnedModeChanged(boolean hasPinnedNotification) {
- if (Log.isLoggable(TAG, Log.WARN)) {
- Log.w(TAG, "onHeadsUpPinnedModeChanged");
- }
- mStatusBarTouchableRegionManager.updateTouchableRegion();
+ public void onDensityOrFontScaleChanged() {
+ updateResources();
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ updateResources();
}
});
}
+ void setup(VisualStabilityManager visualStabilityManager) {
+ mVisualStabilityManager = visualStabilityManager;
+ }
+
public void setAnimationStateHandler(AnimationStateHandler handler) {
mAnimationStateHandler = handler;
}
- private void initResources() {
+ private void updateResources() {
Resources resources = mContext.getResources();
- mStatusBarHeight = resources.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- mHeadsUpInset = mStatusBarHeight + resources.getDimensionPixelSize(
- R.dimen.heads_up_status_bar_padding);
- mDisplayCutoutTouchableRegionSize = resources.getDimensionPixelSize(
- com.android.internal.R.dimen.display_cutout_touchable_region_size);
- }
-
- @Override
- public void onDensityOrFontScaleChanged() {
- super.onDensityOrFontScaleChanged();
- initResources();
- }
-
- @Override
- public void onOverlayChanged() {
- initResources();
+ mHeadsUpInset =
+ resources.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height)
+ + resources.getDimensionPixelSize(R.dimen.heads_up_status_bar_padding);
}
///////////////////////////////////////////////////////////////////////////////////////////////
// Public methods:
/**
+ * Add a listener to receive callbacks onHeadsUpGoingAway
+ */
+ void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) {
+ mHeadsUpPhoneListeners.add(listener);
+ }
+
+ /**
+ * Gets the touchable region needed for heads up notifications. Returns null if no touchable
+ * region is required (ie: no heads up notification currently exists).
+ */
+ @Nullable Region getTouchableRegion() {
+ NotificationEntry topEntry = getTopEntry();
+
+ // This call could be made in an inconsistent state while the pinnedMode hasn't been
+ // updated yet, but callbacks leading out of the headsUp manager, querying it. Let's
+ // therefore also check if the topEntry is null.
+ if (!hasPinnedHeadsUp() || topEntry == null) {
+ return null;
+ } else {
+ if (topEntry.isChildInGroup()) {
+ final NotificationEntry groupSummary =
+ mGroupManager.getGroupSummary(topEntry.getSbn());
+ if (groupSummary != null) {
+ topEntry = groupSummary;
+ }
+ }
+ ExpandableNotificationRow topRow = topEntry.getRow();
+ int[] tmpArray = new int[2];
+ topRow.getLocationOnScreen(tmpArray);
+ int minX = tmpArray[0];
+ int maxX = tmpArray[0] + topRow.getWidth();
+ int height = topRow.getIntrinsicHeight();
+ mTouchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
+ return mTouchableRegion;
+ }
+ }
+
+ /**
* Decides whether a click is invalid for a notification, i.e it has not been shown long enough
* that a user might have consciously clicked on it.
*
* @param key the key of the touched notification
* @return whether the touch is invalid and should be discarded
*/
- public boolean shouldSwallowClick(@NonNull String key) {
+ boolean shouldSwallowClick(@NonNull String key) {
HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
return entry != null && mClock.currentTimeMillis() < entry.mPostTime;
}
@@ -213,39 +221,12 @@
*
* @param isExpanded True to notify expanded, false to notify collapsed.
*/
- public void setIsPanelExpanded(boolean isExpanded) {
+ void setIsPanelExpanded(boolean isExpanded) {
if (isExpanded != mIsExpanded) {
mIsExpanded = isExpanded;
if (isExpanded) {
mHeadsUpGoingAway = false;
}
- mStatusBarTouchableRegionManager.setIsStatusBarExpanded(isExpanded);
- mStatusBarTouchableRegionManager.updateTouchableRegion();
- }
- }
-
- @Override
- public void onStateChanged(int newState) {
- boolean wasKeyguard = mStatusBarState == StatusBarState.KEYGUARD;
- boolean isKeyguard = newState == StatusBarState.KEYGUARD;
- mStatusBarState = newState;
- if (wasKeyguard && !isKeyguard && mKeysToRemoveWhenLeavingKeyguard.size() != 0) {
- String[] keys = mKeysToRemoveWhenLeavingKeyguard.toArray(new String[0]);
- for (String key : keys) {
- removeAlertEntry(key);
- }
- mKeysToRemoveWhenLeavingKeyguard.clear();
- }
- }
-
- @Override
- public void onDozingChanged(boolean isDozing) {
- if (!isDozing) {
- // Let's make sure all huns we got while dozing time out within the normal timeout
- // duration. Otherwise they could get stuck for a very long time
- for (AlertEntry entry : mAlertEntries.values()) {
- entry.updateEntry(true /* updatePostTime */);
- }
}
}
@@ -262,18 +243,16 @@
* Set that we are exiting the headsUp pinned mode, but some notifications might still be
* animating out. This is used to keep the touchable regions in a sane state.
*/
- public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+ void setHeadsUpGoingAway(boolean headsUpGoingAway) {
if (headsUpGoingAway != mHeadsUpGoingAway) {
mHeadsUpGoingAway = headsUpGoingAway;
- if (!headsUpGoingAway) {
- mStatusBarTouchableRegionManager.updateTouchableRegionAfterLayout();
- } else {
- mStatusBarTouchableRegionManager.updateTouchableRegion();
+ for (OnHeadsUpPhoneListenerChange listener : mHeadsUpPhoneListeners) {
+ listener.onHeadsUpGoingAwayStateChanged(headsUpGoingAway);
}
}
}
- public boolean isHeadsUpGoingAway() {
+ boolean isHeadsUpGoingAway() {
return mHeadsUpGoingAway;
}
@@ -346,63 +325,6 @@
dumpInternal(fd, pw, args);
}
- /**
- * Update touch insets to include any area needed for touching a heads up notification.
- *
- * @param info Insets that will include heads up notification touch area after execution.
- */
- @Nullable
- public void updateTouchableRegion(ViewTreeObserver.InternalInsetsInfo info) {
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(calculateTouchableRegion());
- }
-
- public Region calculateTouchableRegion() {
- NotificationEntry topEntry = getTopEntry();
- // This call could be made in an inconsistent state while the pinnedMode hasn't been
- // updated yet, but callbacks leading out of the headsUp manager, querying it. Let's
- // therefore also check if the topEntry is null.
- if (!hasPinnedHeadsUp() || topEntry == null) {
- mTouchableRegion.set(0, 0, mNotificationShadeWindowView.getWidth(), mStatusBarHeight);
- updateRegionForNotch(mTouchableRegion);
-
- } else {
- if (topEntry.isChildInGroup()) {
- final NotificationEntry groupSummary =
- mGroupManager.getGroupSummary(topEntry.getSbn());
- if (groupSummary != null) {
- topEntry = groupSummary;
- }
- }
- ExpandableNotificationRow topRow = topEntry.getRow();
- topRow.getLocationOnScreen(mTmpTwoArray);
- int minX = mTmpTwoArray[0];
- int maxX = mTmpTwoArray[0] + topRow.getWidth();
- int height = topRow.getIntrinsicHeight();
- mTouchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
- }
- return mTouchableRegion;
- }
-
- private void updateRegionForNotch(Region region) {
- WindowInsets windowInsets = mNotificationShadeWindowView.getRootWindowInsets();
- if (windowInsets == null) {
- Log.w(TAG, "StatusBarWindowView is not attached.");
- return;
- }
- DisplayCutout cutout = windowInsets.getDisplayCutout();
- if (cutout == null) {
- return;
- }
-
- // Expand touchable region such that we also catch touches that just start below the notch
- // area.
- Rect bounds = new Rect();
- ScreenDecorations.DisplayCutoutView.boundsFromDirection(cutout, Gravity.TOP, bounds);
- bounds.offset(0, mDisplayCutoutTouchableRegionSize);
- region.union(bounds);
- }
-
@Override
public boolean shouldExtendLifetime(NotificationEntry entry) {
// We should not defer the removal if reordering isn't allowed since otherwise
@@ -411,11 +333,6 @@
return mVisualStabilityManager.isReorderingAllowed() && super.shouldExtendLifetime(entry);
}
- @Override
- public void onConfigChanged(Configuration newConfig) {
- initResources();
- }
-
///////////////////////////////////////////////////////////////////////////////////////////////
// VisualStabilityManager.Callback overrides:
@@ -522,8 +439,7 @@
// time out anyway
&& !entry.showingPulsing()) {
mEntriesToRemoveWhenReorderingAllowed.add(entry);
- mVisualStabilityManager.addReorderingAllowedCallback(
- HeadsUpManagerPhone.this);
+ mVisualStabilityManager.addReorderingAllowedCallback(HeadsUpManagerPhone.this);
} else if (mTrackingHeadsUp) {
mEntriesToRemoveAfterExpand.add(entry);
} else if (mIsAutoHeadsUp && mStatusBarState == StatusBarState.KEYGUARD) {
@@ -626,4 +542,42 @@
public interface AnimationStateHandler {
void setHeadsUpGoingAwayAnimationsAllowed(boolean allowed);
}
+
+ /**
+ * Listener to register for HeadsUpNotification Phone changes.
+ */
+ public interface OnHeadsUpPhoneListenerChange {
+ /**
+ * Called when a heads up notification is 'going away' or no longer 'going away'.
+ * See {@link HeadsUpManagerPhone#setHeadsUpGoingAway}.
+ */
+ void onHeadsUpGoingAwayStateChanged(boolean headsUpGoingAway);
+ }
+
+ private final StateListener mStatusBarStateListener = new StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ boolean wasKeyguard = mStatusBarState == StatusBarState.KEYGUARD;
+ boolean isKeyguard = newState == StatusBarState.KEYGUARD;
+ mStatusBarState = newState;
+ if (wasKeyguard && !isKeyguard && mKeysToRemoveWhenLeavingKeyguard.size() != 0) {
+ String[] keys = mKeysToRemoveWhenLeavingKeyguard.toArray(new String[0]);
+ for (String key : keys) {
+ removeAlertEntry(key);
+ }
+ mKeysToRemoveWhenLeavingKeyguard.clear();
+ }
+ }
+
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ if (!isDozing) {
+ // Let's make sure all huns we got while dozing time out within the normal timeout
+ // duration. Otherwise they could get stuck for a very long time
+ for (AlertEntry entry : mAlertEntries.values()) {
+ entry.updateEntry(true /* updatePostTime */);
+ }
+ }
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index d2186f9..fb7976f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -453,10 +453,11 @@
KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger,
ActivityManager activityManager, ZenModeController zenModeController,
ConfigurationController configurationController,
- FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
+ FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
+ StatusBarTouchableRegionManager statusBarTouchableRegionManager) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
- latencyTracker, flingAnimationUtilsBuilder);
+ latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager);
mView = view;
mMetricsLogger = metricsLogger;
mActivityManager = activityManager;
@@ -704,9 +705,9 @@
private Rect calculateGestureExclusionRect() {
Rect exclusionRect = null;
- Region touchableRegion = mHeadsUpManager.calculateTouchableRegion();
+ Region touchableRegion = mStatusBarTouchableRegionManager.calculateTouchableRegion();
if (isFullyCollapsed() && touchableRegion != null) {
- // Note: The heads up manager also calculates the non-pinned touchable region
+ // Note: The manager also calculates the non-pinned touchable region
exclusionRect = touchableRegion.getBounds();
}
return exclusionRect != null ? exclusionRect : EMPTY_RECT;
@@ -1914,6 +1915,7 @@
boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
if (mPanelExpanded != isExpanded) {
mHeadsUpManager.setIsPanelExpanded(isExpanded);
+ mStatusBarTouchableRegionManager.setPanelExpanded(isExpanded);
mStatusBar.setPanelExpanded(isExpanded);
mPanelExpanded = isExpanded;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index af46f7b..30367ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -73,6 +73,7 @@
protected StatusBar mStatusBar;
protected HeadsUpManagerPhone mHeadsUpManager;
+ protected final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
private float mPeekHeight;
private float mHintDistance;
@@ -206,7 +207,9 @@
FalsingManager falsingManager, DozeLog dozeLog,
KeyguardStateController keyguardStateController,
SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper,
- LatencyTracker latencyTracker, FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
+ LatencyTracker latencyTracker,
+ FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
+ StatusBarTouchableRegionManager statusBarTouchableRegionManager) {
mView = view;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
@@ -251,6 +254,7 @@
R.bool.config_enableNotificationShadeDrag);
mVibratorHelper = vibratorHelper;
mVibrateOnOpening = mResources.getBoolean(R.bool.config_vibrateOnIconAnimation);
+ mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
}
protected void loadDimens() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 301eac2..d8d96c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -356,6 +356,7 @@
private final KeyguardBypassController mKeyguardBypassController;
private final KeyguardStateController mKeyguardStateController;
private final HeadsUpManagerPhone mHeadsUpManager;
+ private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
private final DynamicPrivacyController mDynamicPrivacyController;
private final BypassHeadsUpNotifier mBypassHeadsUpNotifier;
private final FalsingManager mFalsingManager;
@@ -676,7 +677,8 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
- DismissCallbackRegistry dismissCallbackRegistry) {
+ DismissCallbackRegistry dismissCallbackRegistry,
+ StatusBarTouchableRegionManager statusBarTouchableRegionManager) {
super(context);
mNotificationsController = notificationsController;
mLightBarController = lightBarController;
@@ -688,6 +690,7 @@
mKeyguardBypassController = keyguardBypassController;
mKeyguardStateController = keyguardStateController;
mHeadsUpManager = headsUpManagerPhone;
+ mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
mDynamicPrivacyController = dynamicPrivacyController;
mBypassHeadsUpNotifier = bypassHeadsUpNotifier;
mFalsingManager = falsingManager;
@@ -1031,9 +1034,8 @@
CollapsedStatusBarFragment.TAG)
.commit();
- mHeadsUpManager.setUp(mNotificationShadeWindowView, mGroupManager, this,
- mVisualStabilityManager);
- mConfigurationController.addCallback(mHeadsUpManager);
+ mHeadsUpManager.setup(mVisualStabilityManager);
+ mStatusBarTouchableRegionManager.setup(this, mNotificationShadeWindowView);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
mHeadsUpManager.addListener(mVisualStabilityManager);
@@ -2468,6 +2470,12 @@
pw.println(" mHeadsUpManager: null");
}
+ if (mStatusBarTouchableRegionManager != null) {
+ mStatusBarTouchableRegionManager.dump(fd, pw, args);
+ } else {
+ pw.println(" mStatusBarTouchableRegionManager: null");
+ }
+
if (mLightBarController != null) {
mLightBarController.dump(fd, pw, args);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 2485513..693cdd2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -209,7 +209,7 @@
};
mViewHierarchyManager.setUpWithPresenter(this, notifListContainer);
- mEntryManager.setUpWithPresenter(this, notifListContainer, mHeadsUpManager);
+ mEntryManager.setUpWithPresenter(this);
mEntryManager.addNotificationEntryListener(notificationEntryListener);
mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager);
mEntryManager.addNotificationLifetimeExtender(mGutsManager);
@@ -230,8 +230,6 @@
onUserSwitched(mLockscreenUserManager.getCurrentUserId());
});
Dependency.get(ConfigurationController.class).addCallback(this);
-
- notificationAlertingManager.setHeadsUpManager(mHeadsUpManager);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index b8fb6d3..5bab867 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -17,66 +17,187 @@
package com.android.systemui.statusbar.phone;
import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.Log;
+import android.view.DisplayCutout;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
+import android.view.WindowInsets;
-import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
import com.android.systemui.R;
+import com.android.systemui.ScreenDecorations;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
/**
- * Manages what parts of the status bar are touchable. Clients are primarily UI that displays in the
- * status bar even though the UI doesn't look like part of the status bar.
+ * Manages what parts of the status bar are touchable. Clients are primarily UI that display in the
+ * status bar even though the UI doesn't look like part of the status bar. Currently this
+ * includes HeadsUpNotifications and Bubbles.
*/
-public final class StatusBarTouchableRegionManager implements
- OnComputeInternalInsetsListener, ConfigurationListener {
+@Singleton
+public final class StatusBarTouchableRegionManager implements Dumpable {
+ private static final String TAG = "TouchableRegionManager";
- private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
+ private final Context mContext;
private final HeadsUpManagerPhone mHeadsUpManager;
+ private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final BubbleController mBubbleController;
+
private boolean mIsStatusBarExpanded = false;
private boolean mShouldAdjustInsets = false;
- private final StatusBar mStatusBar;
- private final View mNotificationShadeWindowView;
+ private StatusBar mStatusBar;
+ private View mNotificationShadeWindowView;
private View mNotificationPanelView;
private boolean mForceCollapsedUntilLayout = false;
- private final NotificationShadeWindowController mNotificationShadeWindowController;
- public StatusBarTouchableRegionManager(HeadsUpManagerPhone headsUpManager,
- @NonNull StatusBar statusBar,
- @NonNull View notificationShadeWindowView) {
- mHeadsUpManager = headsUpManager;
- mStatusBar = statusBar;
- mNotificationShadeWindowView = notificationShadeWindowView;
- mNotificationShadeWindowController =
- Dependency.get(NotificationShadeWindowController.class);
+ private Region mTouchableRegion = new Region();
+ private int mDisplayCutoutTouchableRegionSize;
+ private int mStatusBarHeight;
- mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
- updateTouchableRegion();
+ @Inject
+ public StatusBarTouchableRegionManager(
+ Context context,
+ NotificationShadeWindowController notificationShadeWindowController,
+ ConfigurationController configurationController,
+ HeadsUpManagerPhone headsUpManager,
+ BubbleController bubbleController
+ ) {
+ mContext = context;
+ initResources();
+ configurationController.addCallback(new ConfigurationListener() {
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ initResources();
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ initResources();
+ }
});
+ mHeadsUpManager = headsUpManager;
+ mHeadsUpManager.addListener(
+ new OnHeadsUpChangedListener() {
+ @Override
+ public void onHeadsUpPinnedModeChanged(boolean hasPinnedNotification) {
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "onHeadsUpPinnedModeChanged");
+ }
+ updateTouchableRegion();
+ }
+ });
+ mHeadsUpManager.addHeadsUpPhoneListener(
+ new HeadsUpManagerPhone.OnHeadsUpPhoneListenerChange() {
+ @Override
+ public void onHeadsUpGoingAwayStateChanged(boolean headsUpGoingAway) {
+ if (!headsUpGoingAway) {
+ updateTouchableRegionAfterLayout();
+ } else {
+ updateTouchableRegion();
+ }
+ }
+ });
+
+ mNotificationShadeWindowController = notificationShadeWindowController;
mNotificationShadeWindowController.setForcePluginOpenListener((forceOpen) -> {
updateTouchableRegion();
});
- Dependency.get(ConfigurationController.class).addCallback(this);
- if (mNotificationShadeWindowView != null) {
- mNotificationPanelView = mNotificationShadeWindowView.findViewById(
- R.id.notification_panel);
+
+ mBubbleController = bubbleController;
+ mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
+ updateTouchableRegion();
+ });
+ }
+
+ protected void setup(
+ @NonNull StatusBar statusBar,
+ @NonNull View notificationShadeWindowView) {
+ mStatusBar = statusBar;
+ mNotificationShadeWindowView = notificationShadeWindowView;
+ mNotificationPanelView = mNotificationShadeWindowView.findViewById(R.id.notification_panel);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("StatusBarTouchableRegionManager state:");
+ pw.print(" mTouchableRegion=");
+ pw.println(mTouchableRegion);
+ }
+
+ /**
+ * Notify that the status bar panel gets expanded or collapsed.
+ *
+ * @param isExpanded True to notify expanded, false to notify collapsed.
+ */
+ void setPanelExpanded(boolean isExpanded) {
+ if (isExpanded != mIsStatusBarExpanded) {
+ mIsStatusBarExpanded = isExpanded;
+ if (isExpanded) {
+ // make sure our state is sane
+ mForceCollapsedUntilLayout = false;
+ }
+ updateTouchableRegion();
}
}
/**
+ * Calculates the touch region needed for heads up notifications, taking into consideration
+ * any existing display cutouts (notch)
+ * @return the heads up notification touch area
+ */
+ Region calculateTouchableRegion() {
+ // Update touchable region for HeadsUp notifications
+ final Region headsUpTouchableRegion = mHeadsUpManager.getTouchableRegion();
+ if (headsUpTouchableRegion != null) {
+ mTouchableRegion.set(headsUpTouchableRegion);
+ } else {
+ // If there aren't any HUNs, update the touch region to the status bar
+ // width/height, potentially adjusting for a display cutout (notch)
+ mTouchableRegion.set(0, 0, mNotificationShadeWindowView.getWidth(),
+ mStatusBarHeight);
+ updateRegionForNotch(mTouchableRegion);
+ }
+
+ // Update touchable region for bubbles
+ Rect bubbleRect = mBubbleController.getTouchableRegion();
+ if (bubbleRect != null) {
+ mTouchableRegion.union(bubbleRect);
+ }
+ return mTouchableRegion;
+ }
+
+ private void initResources() {
+ Resources resources = mContext.getResources();
+ mDisplayCutoutTouchableRegionSize = resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.display_cutout_touchable_region_size);
+ mStatusBarHeight =
+ resources.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+ }
+
+ /**
* Set the touchable portion of the status bar based on what elements are visible.
*/
- public void updateTouchableRegion() {
+ private void updateTouchableRegion() {
boolean hasCutoutInset = (mNotificationShadeWindowView != null)
&& (mNotificationShadeWindowView.getRootWindowInsets() != null)
&& (mNotificationShadeWindowView.getRootWindowInsets().getDisplayCutout() != null);
- boolean shouldObserve =
- mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpManager.isHeadsUpGoingAway()
+ boolean shouldObserve = mHeadsUpManager.hasPinnedHeadsUp()
+ || mHeadsUpManager.isHeadsUpGoingAway()
|| mBubbleController.hasBubbles()
|| mForceCollapsedUntilLayout
|| hasCutoutInset
@@ -87,11 +208,11 @@
if (shouldObserve) {
mNotificationShadeWindowView.getViewTreeObserver()
- .addOnComputeInternalInsetsListener(this);
+ .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
mNotificationShadeWindowView.requestLayout();
} else {
mNotificationShadeWindowView.getViewTreeObserver()
- .removeOnComputeInternalInsetsListener(this);
+ .removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
}
mShouldAdjustInsets = shouldObserve;
}
@@ -99,7 +220,7 @@
/**
* Calls {@code updateTouchableRegion()} after a layout pass completes.
*/
- public void updateTouchableRegionAfterLayout() {
+ private void updateTouchableRegionAfterLayout() {
if (mNotificationPanelView != null) {
mForceCollapsedUntilLayout = true;
mNotificationPanelView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@@ -116,34 +237,38 @@
}
}
- /**
- * Notify that the status bar panel gets expanded or collapsed.
- *
- * @param isExpanded True to notify expanded, false to notify collapsed.
- */
- public void setIsStatusBarExpanded(boolean isExpanded) {
- if (isExpanded != mIsStatusBarExpanded) {
- mIsStatusBarExpanded = isExpanded;
- if (isExpanded) {
- // make sure our state is sane
- mForceCollapsedUntilLayout = false;
- }
- updateTouchableRegion();
+ private void updateRegionForNotch(Region touchableRegion) {
+ WindowInsets windowInsets = mNotificationShadeWindowView.getRootWindowInsets();
+ if (windowInsets == null) {
+ Log.w(TAG, "StatusBarWindowView is not attached.");
+ return;
}
- }
-
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- if (mIsStatusBarExpanded || mStatusBar.isBouncerShowing()) {
- // The touchable region is always the full area when expanded
+ DisplayCutout cutout = windowInsets.getDisplayCutout();
+ if (cutout == null) {
return;
}
- mHeadsUpManager.updateTouchableRegion(info);
-
- Rect bubbleRect = mBubbleController.getTouchableRegion();
- if (bubbleRect != null) {
- info.touchableRegion.union(bubbleRect);
- }
+ // Expand touchable region such that we also catch touches that just start below the notch
+ // area.
+ Rect bounds = new Rect();
+ ScreenDecorations.DisplayCutoutView.boundsFromDirection(cutout, Gravity.TOP, bounds);
+ bounds.offset(0, mDisplayCutoutTouchableRegionSize);
+ touchableRegion.union(bounds);
}
+
+ private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener =
+ new OnComputeInternalInsetsListener() {
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+ if (mIsStatusBarExpanded || mStatusBar.isBouncerShowing()) {
+ // The touchable region is always the full area when expanded
+ return;
+ }
+
+ // Update touch insets to include any area needed for touching features that live in
+ // the status bar (ie: heads up notifications or bubbles)
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ info.touchableRegion.set(calculateTouchableRegion());
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 26459a9..e64f821 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -85,6 +85,7 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
+import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -192,7 +193,8 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
- DismissCallbackRegistry dismissCallbackRegistry) {
+ DismissCallbackRegistry dismissCallbackRegistry,
+ StatusBarTouchableRegionManager statusBarTouchableRegionManager) {
return new StatusBar(
context,
notificationsController,
@@ -267,6 +269,7 @@
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
- dismissCallbackRegistry);
+ dismissCallbackRegistry,
+ statusBarTouchableRegionManager);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/Assert.java b/packages/SystemUI/src/com/android/systemui/util/Assert.java
index f6e921e..3f05657 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Assert.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Assert.java
@@ -24,12 +24,17 @@
* Helper providing common assertions.
*/
public class Assert {
+ private static final Looper sMainLooper = Looper.getMainLooper();
+ private static Looper sTestLooper = null;
@VisibleForTesting
- public static Looper sMainLooper = Looper.getMainLooper();
+ public static void setTestableLooper(Looper testLooper) {
+ sTestLooper = testLooper;
+ }
public static void isMainThread() {
- if (!sMainLooper.isCurrentThread()) {
+ if (!sMainLooper.isCurrentThread()
+ && (sTestLooper == null || !sTestLooper.isCurrentThread())) {
throw new IllegalStateException("should be called from the main thread."
+ " sMainLooper.threadName=" + sMainLooper.getThread().getName()
+ " Thread.currentThread()=" + Thread.currentThread().getName());
@@ -37,7 +42,8 @@
}
public static void isNotMainThread() {
- if (sMainLooper.isCurrentThread()) {
+ if (sMainLooper.isCurrentThread()
+ && (sTestLooper == null || sTestLooper.isCurrentThread())) {
throw new IllegalStateException("should not be called from the main thread.");
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
index 082782d..b6ca8d8e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
@@ -37,7 +37,7 @@
@Test
public void testInflation_doesntCrash() {
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
InjectionInflationController inflationController = new InjectionInflationController(
SystemUIFactory.getInstance().getRootComponent());
Context context = getContext();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index 116f8fc..462b042 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -19,7 +19,6 @@
import android.net.Uri;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
@@ -51,7 +50,7 @@
@Before
public void setUp() throws Exception {
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
InjectionInflationController inflationController = new InjectionInflationController(
SystemUIFactory.getInstance().getRootComponent());
LayoutInflater layoutInflater = inflationController
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
index e4b83cc..bc3c3d9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
@@ -20,14 +20,12 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.Assert;
import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
@@ -50,7 +48,7 @@
@Before
public void setUp() {
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
InjectionInflationController inflationController = new InjectionInflationController(
SystemUIFactory.getInstance().getRootComponent());
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 7e4ba92..befe3e1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -147,6 +147,7 @@
context.addMockSystemService(SubscriptionManager.class, mSubscriptionManager);
mTestableLooper = TestableLooper.get(this);
+ allowTestableLooperAsMainThread();
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(context);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index ffe8c28..471149c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -23,7 +23,6 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.annotation.UiThreadTest;
@@ -33,7 +32,6 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.util.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -52,7 +50,7 @@
public void setUp() throws Exception {
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
mDependency.injectMockDependency(NotificationMediaManager.class);
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
Context context = getContext();
mRow = new NotificationTestHelper(context, mDependency).createRow();
mCallback = mock(ExpandHelper.Callback.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 8d11b54..c912b67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -35,7 +35,6 @@
import android.app.NotificationManager;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Looper;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
@@ -75,8 +74,8 @@
@Before
public void setUp() throws Exception {
- // assume the TestLooper is the main looper for these tests
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ // allow the TestLooper to be asserted as the main thread these tests
+ allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
mFsc = new ForegroundServiceController(mEntryManager, mAppOpsController, mMainHandler);
@@ -93,7 +92,7 @@
public void testAppOpsChangedCalledFromBgThread() {
try {
// WHEN onAppOpChanged is called from a different thread than the MainLooper
- com.android.systemui.util.Assert.sMainLooper = Looper.getMainLooper();
+ disallowTestableLooperAsMainThread();
NotificationEntry entry = createFgEntry();
mFsc.onAppOpChanged(
AppOpsManager.OP_CAMERA,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index fc331d6..689eed9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -77,7 +77,7 @@
@Before
public void setUp() throws Exception {
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
mEventCountdown = new CountDownLatch(1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index c85d600..7ac5443 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -25,6 +25,7 @@
import android.os.ParcelFileDescriptor;
import android.testing.DexmakerShareClassLoaderRule;
import android.testing.LeakCheck;
+import android.testing.TestableLooper;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -32,7 +33,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.util.Assert;
import org.junit.After;
import org.junit.Before;
@@ -86,11 +86,24 @@
public void SysuiTeardown() {
InstrumentationRegistry.registerInstance(mRealInstrumentation,
InstrumentationRegistry.getArguments());
- // Reset the assert's main looper.
- Assert.sMainLooper = Looper.getMainLooper();
+ // Reset the assert's testable looper to null.
+ disallowTestableLooperAsMainThread();
SystemUIFactory.cleanup();
}
+ /**
+ * Tests are run on the TestableLooper; however, there are parts of SystemUI that assert that
+ * the code is run from the main looper. Therefore, we allow the TestableLooper to pass these
+ * assertions since in a test, the TestableLooper is essentially the MainLooper.
+ */
+ protected void allowTestableLooperAsMainThread() {
+ com.android.systemui.util.Assert.setTestableLooper(TestableLooper.get(this).getLooper());
+ }
+
+ protected void disallowTestableLooperAsMainThread() {
+ com.android.systemui.util.Assert.setTestableLooper(null);
+ }
+
protected LeakCheck getLeakCheck() {
return null;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index 6d83ac3..644ed3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -85,7 +85,7 @@
Dependency.get(NotificationLockscreenUserManager.class);
NotificationViewHierarchyManager viewHierarchyManager =
Dependency.get(NotificationViewHierarchyManager.class);
- entryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
+ entryManager.setUpWithPresenter(mPresenter);
entryManager.addNotificationEntryListener(mEntryListener);
gutsManager.setUpWithPresenter(mPresenter, mListContainer,
mCheckSaveListener, mOnSettingsClickListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 60163f2..8e87e0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -54,7 +54,6 @@
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.util.Assert;
import com.google.android.collect.Lists;
@@ -90,7 +89,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
- Assert.sMainLooper = mTestableLooper.getLooper();
+ allowTestableLooperAsMainThread();
mHandler = Handler.createAsync(mTestableLooper.getLooper());
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
index 9d667a9..0a38f16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
@@ -21,7 +21,6 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.widget.FrameLayout;
@@ -46,7 +45,7 @@
@Before
public void setUp() throws Exception {
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
mNotificationTestHelper = new NotificationTestHelper(getContext(), mDependency);
mHostLayout = new FrameLayout(getContext());
mObserver = new AboveShelfObserver(mHostLayout);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index f1fba79..6a6e5c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -98,7 +98,6 @@
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.util.Assert;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.time.FakeSystemClock;
@@ -205,7 +204,7 @@
mCountDownLatch = new CountDownLatch(1);
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
Handler.createAsync(TestableLooper.get(this).getLooper()));
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
@@ -256,7 +255,7 @@
mLeakDetector,
mock(ForegroundServiceDismissalFeatureController.class)
);
- mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
+ mEntryManager.setUpWithPresenter(mPresenter);
mEntryManager.addNotificationEntryListener(mEntryListener);
mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index 1116a33..97e0a31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -32,7 +32,6 @@
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.annotation.UiThreadTest;
@@ -79,7 +78,7 @@
@Before
public void setUp() throws Exception {
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
index 0e730e5..d522f90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
@@ -22,11 +22,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.NotificationGroupManager
import com.android.systemui.util.leak.LeakDetector
-
import java.util.concurrent.CountDownLatch
/**
@@ -53,11 +50,9 @@
}
fun setUpForTest(
- presenter: NotificationPresenter?,
- listContainer: NotificationListContainer?,
- headsUpManager: HeadsUpManagerPhone?
+ presenter: NotificationPresenter?
) {
- super.setUpWithPresenter(presenter, listContainer, headsUpManager)
+ super.setUpWithPresenter(presenter)
}
fun setActiveNotificationList(activeList: List<NotificationEntry>) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 12e9d31..605b59e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -77,7 +77,6 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
-import com.android.systemui.util.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -129,7 +128,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 27ca18c..e570ab8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -51,7 +51,6 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
-import com.android.systemui.util.Assert;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -101,7 +100,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
mListBuilder = new ShadeListBuilder(mSystemClock, mLogger, mock(DumpController.class));
mListBuilder.setOnRenderListListener(mOnRenderListListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
index eb1af7c..67b1aad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
@@ -44,7 +44,6 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
-import com.android.systemui.util.Assert;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -84,7 +83,7 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
mForegroundCoordinator =
new ForegroundCoordinator(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index a891810..e960185 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -39,7 +39,6 @@
import android.app.AppOpsManager;
import android.app.NotificationChannel;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.ArraySet;
import android.view.NotificationHeaderView;
@@ -79,7 +78,7 @@
@Before
public void setUp() throws Exception {
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
mGroupRow = mNotificationTestHelper.createGroup();
mGroupRow.setHeadsUpAnimatingAwayListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index a8c438a..481bac2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -49,7 +49,6 @@
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.util.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -75,7 +74,7 @@
@Before
public void setUp() {
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
mDependency.injectMockDependency(BubbleController.class);
when(mGutsManager.openGuts(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index bbb6723..54c0bde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -74,7 +74,6 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.util.Assert;
import org.junit.Before;
import org.junit.Ignore;
@@ -118,7 +117,7 @@
@Before
public void setUp() {
mTestableLooper = TestableLooper.get(this);
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
mDependency.injectTestDependency(DeviceProvisionedController.class,
mDeviceProvisionedController);
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 9a52ee8..5a89fc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -56,6 +56,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -102,8 +103,8 @@
mStatusBarStateController = mock(StatusBarStateController.class);
mGroupManager = new NotificationGroupManager(mStatusBarStateController);
mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarStateController,
- mock(KeyguardBypassController.class));
- mHeadsUpManager.setUp(null, mGroupManager, null, null);
+ mock(KeyguardBypassController.class), mock(NotificationGroupManager.class),
+ mock(ConfigurationControllerImpl.class));
mGroupManager.setHeadsUpManager(mHeadsUpManager);
NotificationContentInflater contentBinder = new NotificationContentInflater(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
index 0790cb7..b661b28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.row.wrapper;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.widget.RemoteViews;
@@ -43,7 +42,7 @@
@Before
public void setUp() throws Exception {
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
mRow = new NotificationTestHelper(mContext, mDependency).createRow();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
index 038eff7..69e4f22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
@@ -26,7 +26,6 @@
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.widget.RemoteViews;
@@ -64,7 +63,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
index 9567f33..830e8d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.widget.LinearLayout;
@@ -31,7 +30,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.util.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -48,7 +46,7 @@
@Before
public void setup() throws Exception {
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
mView = mock(View.class);
mRow = new NotificationTestHelper(getContext(), mDependency).createRow();
mNotificationViewWrapper = new TestableNotificationViewWrapper(mContext, mView, mRow);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 1773175..a2029c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.stack;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.NotificationHeaderView;
import android.view.View;
@@ -44,7 +43,7 @@
@Before
public void setUp() throws Exception {
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
mGroup = mNotificationTestHelper.createGroup();
mChildrenContainer = mGroup.getChildrenContainer();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 2d1bc78..ba2b946 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -24,7 +24,6 @@
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
@@ -66,7 +65,7 @@
mRoundnessManager = new NotificationRoundnessManager(
mBypassController,
new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext));
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
NotificationTestHelper testHelper = new NotificationTestHelper(getContext(), mDependency);
mFirst = testHelper.createRow();
mFirst.setHeadsUpAnimatingAwayListener(animatingAway
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 9ccee75..0cb6585 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -144,7 +144,7 @@
@Before
@UiThreadTest
public void setUp() throws Exception {
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
mOriginalInterruptionModelSetting = Settings.Secure.getInt(mContext.getContentResolver(),
NOTIFICATION_NEW_INTERRUPTION_MODEL, 0);
@@ -185,7 +185,7 @@
mock(LeakDetector.class),
mock(ForegroundServiceDismissalFeatureController.class)
);
- mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager);
+ mEntryManager.setUpForTest(mock(NotificationPresenter.class));
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
NotificationShelf notificationShelf = mock(NotificationShelf.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index f71d0fc..a74657e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -23,7 +23,6 @@
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.widget.TextView;
@@ -69,7 +68,7 @@
@Before
public void setUp() throws Exception {
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
NotificationTestHelper testHelper = new NotificationTestHelper(getContext(), mDependency);
mFirst = testHelper.createRow();
mDependency.injectTestDependency(DarkIconDispatcher.class, mDarkIconDispatcher);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 50d8bf0b0..6d642ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -62,16 +62,21 @@
@Mock private StatusBar mBar;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private KeyguardBypassController mBypassController;
+ @Mock private ConfigurationControllerImpl mConfigurationController;
private boolean mLivesPastNormalTime;
private final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone {
- TestableHeadsUpManagerPhone(Context context, View notificationShadeWindowView,
- NotificationGroupManager groupManager, StatusBar bar,
+ TestableHeadsUpManagerPhone(
+ Context context,
+ NotificationGroupManager groupManager,
VisualStabilityManager vsManager,
StatusBarStateController statusBarStateController,
- KeyguardBypassController keyguardBypassController) {
- super(context, statusBarStateController, keyguardBypassController);
- setUp(notificationShadeWindowView, groupManager, bar, vsManager);
+ KeyguardBypassController keyguardBypassController,
+ ConfigurationController configurationController
+ ) {
+ super(context, statusBarStateController, keyguardBypassController,
+ groupManager, configurationController);
+ setup(vsManager);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
}
@@ -91,8 +96,8 @@
mDependency.injectMockDependency(BubbleController.class);
mDependency.injectMockDependency(NotificationShadeWindowController.class);
mDependency.injectMockDependency(ConfigurationController.class);
- mHeadsUpManager = new TestableHeadsUpManagerPhone(mContext, mNotificationShadeWindowView,
- mGroupManager, mBar, mVSManager, mStatusBarStateController, mBypassController);
+ mHeadsUpManager = new TestableHeadsUpManagerPhone(mContext, mGroupManager, mVSManager,
+ mStatusBarStateController, mBypassController, mConfigurationController);
super.setUp();
mHeadsUpManager.mHandler = mTestHandler;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 67b8e07..35971bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -96,7 +96,7 @@
@Before
public void setup() {
- com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor);
mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 5fb7159..13bf38c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -138,6 +138,8 @@
@Mock
private NotificationEntryManager mNotificationEntryManager;
@Mock
+ private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+ @Mock
private KeyguardStateController mKeyguardStateController;
@Mock
private DozeLog mDozeLog;
@@ -221,7 +223,7 @@
mDozeParameters, mCommandQueue, mVibratorHelper,
mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
mMetricsLogger, mActivityManager, mZenModeController, mConfigurationController,
- mFlingAnimationUtilsBuilder);
+ mFlingAnimationUtilsBuilder, mStatusBarTouchableRegionManager);
mNotificationPanelViewController.initDependencies(mStatusBar, mGroupManager,
mNotificationShelf, mNotificationAreaController, mScrimController);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index db17a6e..e5ee439 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -236,6 +236,7 @@
@Mock private LightsOutNotifController mLightsOutNotifController;
@Mock private ViewMediatorCallback mViewMediatorCallback;
@Mock private DismissCallbackRegistry mDismissCallbackRegistry;
+ @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
@Mock private ScreenPinningRequest mScreenPinningRequest;
@Mock private LockscreenLockIconController mLockscreenLockIconController;
@Mock private StatusBarNotificationActivityStarter.Builder
@@ -397,7 +398,8 @@
mKeyguardDismissUtil,
mExtensionController,
mUserInfoControllerImpl,
- mDismissCallbackRegistry);
+ mDismissCallbackRegistry,
+ mStatusBarTouchableRegionManager);
when(mNotificationShadeWindowView.findViewById(R.id.lock_icon_container)).thenReturn(
mLockIconContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index df62254..86add98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -43,7 +43,6 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.util.Assert;
import org.junit.After;
import org.junit.Before;
@@ -74,7 +73,7 @@
@Before
public void setUp() throws Exception {
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
mDependency.injectTestDependency(RemoteInputQuickSettingsDisabler.class,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
index 20dcbb7..1ff9548 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
@@ -21,14 +21,12 @@
import static org.mockito.Mockito.verify;
import android.animation.Animator;
-import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -45,7 +43,7 @@
@Before
public void setup() {
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
KeepAwakeAnimationListener.sWakeLock = mWakeLock;
mKeepAwakeAnimationListener = new KeepAwakeAnimationListener(getContext());
@@ -63,7 +61,10 @@
@Test(expected = IllegalStateException.class)
public void initThrows_onNonMainThread() {
- Assert.sMainLooper = Looper.getMainLooper();
+ disallowTestableLooperAsMainThread();
+
+ // we are creating the KeepAwakeAnimationListener from the TestableLooper, not the main
+ // looper, so we expect an IllegalStateException:
new KeepAwakeAnimationListener(getContext());
}
}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index bfa962a..fd9f713 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -212,7 +212,7 @@
new Thread(() -> {
while (true) {
try {
- Thread.sleep(200);
+ Thread.sleep(CONNECTOR_POLL_INTERVAL_MILLIS);
} catch (InterruptedException e) {
// Not much to do here, the system needs to wait for the connector
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 697e5d7..f544517 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2624,6 +2624,8 @@
mService.getServicePackageName(), mComponentName,
serviceLabel, serviceIcon, this, id, mCompatMode);
+ mService.logDatasetShown(id, mClientState);
+
synchronized (mLock) {
if (mUiShownTime == 0) {
// Log first time UI is shown.
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 63d0924..fde654d 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -69,6 +69,25 @@
public static final int PACKAGE_COMPANION = 14;
public static final int PACKAGE_RETAIL_DEMO = 15;
+ @IntDef(flag = true, prefix = "RESOLVE_", value = {
+ RESOLVE_NON_BROWSER_ONLY,
+ RESOLVE_NON_RESOLVER_ONLY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PrivateResolveFlags {}
+
+ /**
+ * Internal {@link #resolveIntent(Intent, String, int, int, int, boolean, int)} flag:
+ * only match components that contain a generic web intent filter.
+ */
+ public static final int RESOLVE_NON_BROWSER_ONLY = 0x00000001;
+
+ /**
+ * Internal {@link #resolveIntent(Intent, String, int, int, int, boolean, int)} flag: do not
+ * match to the resolver.
+ */
+ public static final int RESOLVE_NON_RESOLVER_ONLY = 0x00000002;
+
@IntDef(value = {
INTEGRITY_VERIFICATION_ALLOW,
INTEGRITY_VERIFICATION_REJECT,
@@ -507,7 +526,8 @@
* Resolves an activity intent, allowing instant apps to be resolved.
*/
public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
- int flags, int userId, boolean resolveForStart, int filterCallingUid);
+ int flags, @PrivateResolveFlags int privateResolveFlags, int userId,
+ boolean resolveForStart, int filterCallingUid);
/**
* Resolves a service intent, allowing instant apps to be resolved.
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0f36260..7083281 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -4791,7 +4791,7 @@
return false;
}
- return vpn.startAlwaysOnVpn();
+ return vpn.startAlwaysOnVpn(mKeyStore);
}
}
@@ -4806,7 +4806,7 @@
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
return false;
}
- return vpn.isAlwaysOnPackageSupported(packageName);
+ return vpn.isAlwaysOnPackageSupported(packageName, mKeyStore);
}
}
@@ -4827,11 +4827,11 @@
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
return false;
}
- if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist)) {
+ if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist, mKeyStore)) {
return false;
}
if (!startAlwaysOnVpn(userId)) {
- vpn.setAlwaysOnPackage(null, false, null);
+ vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
return false;
}
}
@@ -5017,7 +5017,7 @@
loge("Starting user already has a VPN");
return;
}
- userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId);
+ userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId, mKeyStore);
mVpns.put(userId, userVpn);
if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
updateLockdownVpn();
@@ -5088,7 +5088,7 @@
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
Slog.d(TAG, "Restarting always-on VPN package " + packageName + " for user "
+ userId);
- vpn.startAlwaysOnVpn();
+ vpn.startAlwaysOnVpn(mKeyStore);
}
}
}
@@ -5110,7 +5110,7 @@
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user "
+ userId);
- vpn.setAlwaysOnPackage(null, false, null);
+ vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
}
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 97b5eaa..430a5b9 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -79,6 +79,7 @@
import android.os.TransactionTooLargeException;
import android.os.UserHandle;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
@@ -187,6 +188,9 @@
AppWidgetManagerInternal mAppWidgetManagerInternal;
+ // white listed packageName.
+ ArraySet<String> mWhiteListAllowWhileInUsePermissionInFgs = new ArraySet<>();
+
final Runnable mLastAnrDumpClearer = new Runnable() {
@Override public void run() {
synchronized (mAm) {
@@ -389,6 +393,20 @@
AppStateTracker ast = LocalServices.getService(AppStateTracker.class);
ast.addListener(new ForcedStandbyListener());
mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class);
+ setWhiteListAllowWhileInUsePermissionInFgs();
+ }
+
+ private void setWhiteListAllowWhileInUsePermissionInFgs() {
+ final String attentionServicePackageName =
+ mAm.mContext.getPackageManager().getAttentionServicePackageName();
+ if (!TextUtils.isEmpty(attentionServicePackageName)) {
+ mWhiteListAllowWhileInUsePermissionInFgs.add(attentionServicePackageName);
+ }
+ final String systemCaptionsServicePackageName =
+ mAm.mContext.getPackageManager().getSystemCaptionsServicePackageName();
+ if (!TextUtils.isEmpty(systemCaptionsServicePackageName)) {
+ mWhiteListAllowWhileInUsePermissionInFgs.add(systemCaptionsServicePackageName);
+ }
}
ServiceRecord getServiceByNameLocked(ComponentName name, int callingUser) {
@@ -4634,6 +4652,12 @@
return true;
}
+ final boolean isWhiteListedPackage =
+ mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage);
+ if (isWhiteListedPackage) {
+ return true;
+ }
+
r.mInfoDenyWhileInUsePermissionInFgs =
"Background FGS start while-in-use permission restriction [callingPackage: "
+ callingPackage
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index fabe92db..8fbe923 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -19,12 +19,16 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
import android.app.ActivityThread;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.OnPropertiesChangedListener;
import android.provider.DeviceConfig.Properties;
@@ -33,6 +37,7 @@
import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Slog;
+import android.util.SparseArray;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -289,6 +294,12 @@
// started, the restriction is on while-in-use permissions.)
volatile boolean mFlagBackgroundFgsStartRestrictionEnabled = true;
+ /**
+ * UserId to Assistant ComponentName mapping.
+ * Per user Assistant ComponentName is from {@link android.provider.Settings.Secure#ASSISTANT}
+ */
+ SparseArray<ComponentName> mAssistants = new SparseArray<>();
+
private final ActivityManagerService mService;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -364,6 +375,8 @@
Settings.Global.getUriFor(
Settings.Global.FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED);
+ private static final Uri ASSISTANT_URI = Settings.Secure.getUriFor(Settings.Secure.ASSISTANT);
+
private static final Uri ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI =
Settings.Global.getUriFor(Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS);
@@ -430,6 +443,8 @@
mResolver.registerContentObserver(ACTIVITY_STARTS_LOGGING_ENABLED_URI, false, this);
mResolver.registerContentObserver(FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED_URI,
false, this);
+ mResolver.registerContentObserver(ASSISTANT_URI, false, this,
+ UserHandle.USER_ALL);
if (mSystemServerAutomaticHeapDumpEnabled) {
mResolver.registerContentObserver(ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI,
false, this);
@@ -445,6 +460,7 @@
// The following read from Settings.
updateActivityStartsLoggingEnabled();
updateForegroundServiceStartsLoggingEnabled();
+ updateAssistant();
}
private void loadDeviceConfigConstants() {
@@ -476,6 +492,8 @@
updateForegroundServiceStartsLoggingEnabled();
} else if (ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI.equals(uri)) {
updateEnableAutomaticSystemServerHeapDumps();
+ } else if (ASSISTANT_URI.equals(uri)) {
+ updateAssistant();
}
}
@@ -573,6 +591,31 @@
Settings.Global.FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED, 1) == 1;
}
+ private void updateAssistant() {
+ final List<UserInfo> users =
+ mService.mContext.getSystemService(UserManager.class).getUsers();
+ SparseArray<ComponentName> componentNames = new SparseArray<>();
+ for (int i = 0; i < users.size(); i++) {
+ final int userId = users.get(i).id;
+ final String str = Settings.Secure.getStringForUser(mResolver,
+ Settings.Secure.ASSISTANT, userId);
+ if (!TextUtils.isEmpty(str)) {
+ componentNames.put(userId, ComponentName.unflattenFromString(str));
+ }
+ }
+ synchronized (mService) {
+ for (int i = 0; i < mAssistants.size(); i++) {
+ mService.mServices.mWhiteListAllowWhileInUsePermissionInFgs.remove(
+ mAssistants.valueAt(i).getPackageName());
+ }
+ mAssistants = componentNames;
+ for (int i = 0; i < mAssistants.size(); i++) {
+ mService.mServices.mWhiteListAllowWhileInUsePermissionInFgs.add(
+ mAssistants.valueAt(i).getPackageName());
+ }
+ }
+ }
+
private void updateBackgroundFgsStartsRestriction() {
mFlagBackgroundFgsStartRestrictionEnabled = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -581,9 +624,6 @@
}
private void updateOomAdjUpdatePolicy() {
-
-
-
OOMADJ_UPDATE_QUICK = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_OOMADJ_UPDATE_POLICY,
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index a651d9d..a2ae678 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1994,12 +1994,11 @@
if (app.hasForegroundServices()) {
capability |= capabilityFromFGS;
} else if (!ActivityManager.isProcStateBackground(procState)) {
- // procState higher than PROCESS_STATE_TRANSIENT_BACKGROUND implicitly has
+ // procState higher than PROCESS_STATE_BOUND_FOREGROUND_SERVICE implicitly has
// camera/microphone capability
if (procState == PROCESS_STATE_FOREGROUND_SERVICE && procStateFromFGSClient) {
// if the FGS state is passed down from client, do not grant implicit capabilities.
} else {
- //TODO: remove this line when enforcing the feature.
capability |= PROCESS_CAPABILITY_ALL_IMPLICIT;
}
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index a934f22..40acfe1 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -536,9 +536,8 @@
// The FGS has the location capability, but due to FGS BG start
// restriction it lost the capability, use temp location capability
// to mark this case.
- // TODO change to MODE_IGNORED when enforcing the feature.
maybeShowWhileInUseDebugToast(op, mode);
- return AppOpsManager.MODE_ALLOWED;
+ return AppOpsManager.MODE_IGNORED;
} else {
return AppOpsManager.MODE_IGNORED;
}
@@ -546,17 +545,15 @@
if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
return AppOpsManager.MODE_ALLOWED;
} else {
- //TODO change to MODE_IGNORED when enforcing the feature.
maybeShowWhileInUseDebugToast(op, mode);
- return AppOpsManager.MODE_ALLOWED;
+ return AppOpsManager.MODE_IGNORED;
}
case OP_RECORD_AUDIO:
if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) {
return AppOpsManager.MODE_ALLOWED;
} else {
- //TODO change to MODE_IGNORED when enforcing the feature.
maybeShowWhileInUseDebugToast(op, mode);
- return AppOpsManager.MODE_ALLOWED;
+ return AppOpsManager.MODE_IGNORED;
}
default:
return AppOpsManager.MODE_ALLOWED;
@@ -571,17 +568,15 @@
if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
return AppOpsManager.MODE_ALLOWED;
} else {
- //TODO change to MODE_IGNORED when enforcing the feature.
maybeShowWhileInUseDebugToast(op, mode);
- return AppOpsManager.MODE_ALLOWED;
+ return AppOpsManager.MODE_IGNORED;
}
case OP_RECORD_AUDIO:
if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) {
return AppOpsManager.MODE_ALLOWED;
} else {
- //TODO change to MODE_IGNORED when enforcing the feature.
maybeShowWhileInUseDebugToast(op, mode);
- return AppOpsManager.MODE_ALLOWED;
+ return AppOpsManager.MODE_IGNORED;
}
default:
return MODE_ALLOWED;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 77f4093..3138639 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -216,14 +216,14 @@
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
* only applies to {@link VpnService} connections.
*/
- private boolean mAlwaysOn = false;
+ @VisibleForTesting protected boolean mAlwaysOn = false;
/**
* Whether to disable traffic outside of this VPN even when the VPN is not connected. System
* apps can still bypass by choosing explicit networks. Has no effect if {@link mAlwaysOn} is
- * not set.
+ * not set. Applies to all types of VPNs.
*/
- private boolean mLockdown = false;
+ @VisibleForTesting protected boolean mLockdown = false;
/**
* Set of packages in addition to the VPN app itself that can access the network directly when
@@ -252,14 +252,14 @@
private final int mUserHandle;
public Vpn(Looper looper, Context context, INetworkManagementService netService,
- @UserIdInt int userHandle) {
- this(looper, context, netService, userHandle,
+ @UserIdInt int userHandle, @NonNull KeyStore keyStore) {
+ this(looper, context, netService, userHandle, keyStore,
new SystemServices(context), new Ikev2SessionCreator());
}
@VisibleForTesting
protected Vpn(Looper looper, Context context, INetworkManagementService netService,
- int userHandle, SystemServices systemServices,
+ int userHandle, @NonNull KeyStore keyStore, SystemServices systemServices,
Ikev2SessionCreator ikev2SessionCreator) {
mContext = context;
mNetd = netService;
@@ -285,7 +285,7 @@
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
updateCapabilities(null /* defaultNetwork */);
- loadAlwaysOnPackage();
+ loadAlwaysOnPackage(keyStore);
}
/**
@@ -437,23 +437,36 @@
/**
* Checks if a VPN app supports always-on mode.
*
- * In order to support the always-on feature, an app has to
+ * <p>In order to support the always-on feature, an app has to either have an installed
+ * PlatformVpnProfile, or:
+ *
* <ul>
- * <li>target {@link VERSION_CODES#N API 24} or above, and
- * <li>not opt out through the {@link VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}
- * meta-data field.
+ * <li>target {@link VERSION_CODES#N API 24} or above, and
+ * <li>not opt out through the {@link VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}
+ * meta-data field.
* </ul>
*
* @param packageName the canonical package name of the VPN app
+ * @param keyStore the keystore instance to use for checking if the app has a Platform VPN
+ * profile installed.
* @return {@code true} if and only if the VPN app exists and supports always-on mode
*/
- public boolean isAlwaysOnPackageSupported(String packageName) {
+ public boolean isAlwaysOnPackageSupported(String packageName, @NonNull KeyStore keyStore) {
enforceSettingsPermission();
if (packageName == null) {
return false;
}
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ if (getVpnProfilePrivileged(packageName, keyStore) != null) {
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+
PackageManager pm = mContext.getPackageManager();
ApplicationInfo appInfo = null;
try {
@@ -485,27 +498,31 @@
}
/**
- * Configures an always-on VPN connection through a specific application.
- * This connection is automatically granted and persisted after a reboot.
+ * Configures an always-on VPN connection through a specific application. This connection is
+ * automatically granted and persisted after a reboot.
*
- * <p>The designated package should exist and declare a {@link VpnService} in its
- * manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
- * otherwise the call will fail.
+ * <p>The designated package should either have a PlatformVpnProfile installed, or declare a
+ * {@link VpnService} in its manifest guarded by {@link
+ * android.Manifest.permission.BIND_VPN_SERVICE}, otherwise the call will fail.
*
* <p>Note that this method does not check if the VPN app supports always-on mode. The check is
- * delayed to {@link #startAlwaysOnVpn()}, which is always called immediately after this
- * method in {@link android.net.IConnectivityManager#setAlwaysOnVpnPackage}.
+ * delayed to {@link #startAlwaysOnVpn()}, which is always called immediately after this method
+ * in {@link android.net.IConnectivityManager#setAlwaysOnVpnPackage}.
*
* @param packageName the package to designate as always-on VPN supplier.
* @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
* @param lockdownWhitelist packages to be whitelisted from lockdown.
+ * @param keyStore the Keystore instance to use for checking of PlatformVpnProfile(s)
* @return {@code true} if the package has been set as always-on, {@code false} otherwise.
*/
public synchronized boolean setAlwaysOnPackage(
- String packageName, boolean lockdown, List<String> lockdownWhitelist) {
+ @Nullable String packageName,
+ boolean lockdown,
+ @Nullable List<String> lockdownWhitelist,
+ @NonNull KeyStore keyStore) {
enforceControlPermissionOrInternalCaller();
- if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownWhitelist)) {
+ if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownWhitelist, keyStore)) {
saveAlwaysOnPackage();
return true;
}
@@ -513,20 +530,22 @@
}
/**
- * Configures an always-on VPN connection through a specific application, the same as
- * {@link #setAlwaysOnPackage}.
+ * Configures an always-on VPN connection through a specific application, the same as {@link
+ * #setAlwaysOnPackage}.
*
- * Does not perform permission checks. Does not persist any of the changes to storage.
+ * <p>Does not perform permission checks. Does not persist any of the changes to storage.
*
* @param packageName the package to designate as always-on VPN supplier.
* @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
* @param lockdownWhitelist packages to be whitelisted from lockdown. This is only used if
- * {@code lockdown} is {@code true}. Packages must not contain commas.
+ * {@code lockdown} is {@code true}. Packages must not contain commas.
+ * @param keyStore the system keystore instance to check for profiles
* @return {@code true} if the package has been set as always-on, {@code false} otherwise.
*/
@GuardedBy("this")
private boolean setAlwaysOnPackageInternal(
- String packageName, boolean lockdown, List<String> lockdownWhitelist) {
+ @Nullable String packageName, boolean lockdown,
+ @Nullable List<String> lockdownWhitelist, @NonNull KeyStore keyStore) {
if (VpnConfig.LEGACY_VPN.equals(packageName)) {
Log.w(TAG, "Not setting legacy VPN \"" + packageName + "\" as always-on.");
return false;
@@ -542,11 +561,18 @@
}
if (packageName != null) {
- // TODO: Give the minimum permission possible; if there is a Platform VPN profile, only
- // grant ACTIVATE_PLATFORM_VPN.
- // Pre-authorize new always-on VPN package. Grant the full ACTIVATE_VPN appop, allowing
- // both VpnService and Platform VPNs.
- if (!setPackageAuthorization(packageName, VpnManager.TYPE_VPN_SERVICE)) {
+ final VpnProfile profile;
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ profile = getVpnProfilePrivileged(packageName, keyStore);
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+
+ // Pre-authorize new always-on VPN package.
+ final int grantType =
+ (profile == null) ? VpnManager.TYPE_VPN_SERVICE : VpnManager.TYPE_VPN_PLATFORM;
+ if (!setPackageAuthorization(packageName, grantType)) {
return false;
}
mAlwaysOn = true;
@@ -611,11 +637,9 @@
}
}
- /**
- * Load the always-on package and lockdown config from Settings.Secure
- */
+ /** Load the always-on package and lockdown config from Settings. */
@GuardedBy("this")
- private void loadAlwaysOnPackage() {
+ private void loadAlwaysOnPackage(@NonNull KeyStore keyStore) {
final long token = Binder.clearCallingIdentity();
try {
final String alwaysOnPackage = mSystemServices.settingsSecureGetStringForUser(
@@ -626,17 +650,21 @@
Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST, mUserHandle);
final List<String> whitelistedPackages = TextUtils.isEmpty(whitelistString)
? Collections.emptyList() : Arrays.asList(whitelistString.split(","));
- setAlwaysOnPackageInternal(alwaysOnPackage, alwaysOnLockdown, whitelistedPackages);
+ setAlwaysOnPackageInternal(
+ alwaysOnPackage, alwaysOnLockdown, whitelistedPackages, keyStore);
} finally {
Binder.restoreCallingIdentity(token);
}
}
/**
+ * Starts the currently selected always-on VPN
+ *
+ * @param keyStore the keyStore instance for looking up PlatformVpnProfile(s)
* @return {@code true} if the service was started, the service was already connected, or there
- * was no always-on VPN to start. {@code false} otherwise.
+ * was no always-on VPN to start. {@code false} otherwise.
*/
- public boolean startAlwaysOnVpn() {
+ public boolean startAlwaysOnVpn(@NonNull KeyStore keyStore) {
final String alwaysOnPackage;
synchronized (this) {
alwaysOnPackage = getAlwaysOnPackage();
@@ -645,8 +673,8 @@
return true;
}
// Remove always-on VPN if it's not supported.
- if (!isAlwaysOnPackageSupported(alwaysOnPackage)) {
- setAlwaysOnPackage(null, false, null);
+ if (!isAlwaysOnPackageSupported(alwaysOnPackage, keyStore)) {
+ setAlwaysOnPackage(null, false, null, keyStore);
return false;
}
// Skip if the service is already established. This isn't bulletproof: it's not bound
@@ -657,10 +685,24 @@
}
}
- // Tell the OS that background services in this app need to be allowed for
- // a short time, so we can bootstrap the VPN service.
final long oldId = Binder.clearCallingIdentity();
try {
+ // Prefer VPN profiles, if any exist.
+ VpnProfile profile = getVpnProfilePrivileged(alwaysOnPackage, keyStore);
+ if (profile != null) {
+ startVpnProfilePrivileged(profile, alwaysOnPackage);
+
+ // If the above startVpnProfilePrivileged() call returns, the Ikev2VpnProfile was
+ // correctly parsed, and the VPN has started running in a different thread. The only
+ // other possibility is that the above call threw an exception, which will be
+ // caught below, and returns false (clearing the always-on VPN). Once started, the
+ // Platform VPN cannot permanantly fail, and is resiliant to temporary failures. It
+ // will continue retrying until shut down by the user, or always-on is toggled off.
+ return true;
+ }
+
+ // Tell the OS that background services in this app need to be allowed for
+ // a short time, so we can bootstrap the VPN service.
DeviceIdleInternal idleController =
LocalServices.getService(DeviceIdleInternal.class);
idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage,
@@ -675,6 +717,9 @@
Log.e(TAG, "VpnService " + serviceIntent + " failed to start", e);
return false;
}
+ } catch (Exception e) {
+ Log.e(TAG, "Error starting always-on VPN", e);
+ return false;
} finally {
Binder.restoreCallingIdentity(oldId);
}
@@ -2820,6 +2865,10 @@
return isVpnProfilePreConsented(mContext, packageName);
}
+ private boolean isCurrentIkev2VpnLocked(@NonNull String packageName) {
+ return isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner;
+ }
+
/**
* Deletes an app-provisioned VPN profile.
*
@@ -2836,6 +2885,17 @@
Binder.withCleanCallingIdentity(
() -> {
+ // If this profile is providing the current VPN, turn it off, disabling
+ // always-on as well if enabled.
+ if (isCurrentIkev2VpnLocked(packageName)) {
+ if (mAlwaysOn) {
+ // Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
+ setAlwaysOnPackage(null, false, null, keyStore);
+ } else {
+ prepareInternal(VpnConfig.LEGACY_VPN);
+ }
+ }
+
keyStore.delete(getProfileNameForPackage(packageName), Process.SYSTEM_UID);
});
}
@@ -2946,11 +3006,9 @@
// To stop the VPN profile, the caller must be the current prepared package and must be
// running an Ikev2VpnProfile.
- if (!isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner) {
- return;
+ if (isCurrentIkev2VpnLocked(packageName)) {
+ prepareInternal(VpnConfig.LEGACY_VPN);
}
-
- prepareInternal(VpnConfig.LEGACY_VPN);
}
/**
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 6d130d9..4a1afb2 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1216,7 +1216,7 @@
for (SyncOperation op: ops) {
if (op.isPeriodic && op.target.matchesSpec(target)) {
periodicSyncs.add(new PeriodicSync(op.target.account, op.target.provider,
- op.extras, op.periodMillis / 1000, op.flexMillis / 1000));
+ op.getClonedExtras(), op.periodMillis / 1000, op.flexMillis / 1000));
}
}
@@ -1478,7 +1478,7 @@
Slog.e(TAG, "Can't schedule null sync operation.");
return;
}
- if (!syncOperation.ignoreBackoff()) {
+ if (!syncOperation.hasIgnoreBackoff()) {
Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(syncOperation.target);
if (backoff == null) {
Slog.e(TAG, "Couldn't find backoff values for "
@@ -1631,7 +1631,7 @@
getSyncStorageEngine().markPending(syncOperation.target, true);
}
- if (syncOperation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING)) {
+ if (syncOperation.hasRequireCharging()) {
b.setRequiresCharging(true);
}
@@ -1686,7 +1686,7 @@
List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (!op.isPeriodic && op.target.matchesSpec(info)
- && syncExtrasEquals(extras, op.extras, false)) {
+ && op.areExtrasEqual(extras, /*includeSyncSettings=*/ false)) {
cancelJob(op, "cancelScheduledSyncOperation");
}
}
@@ -1704,15 +1704,9 @@
Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation);
}
- // The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given
- // request. Retries of the request will always honor the backoff, so clear the
- // flag in case we retry this request.
- if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) {
- operation.extras.remove(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF);
- }
+ operation.enableBackoff();
- if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)
- && !syncResult.syncAlreadyInProgress) {
+ if (operation.hasDoNotRetry() && !syncResult.syncAlreadyInProgress) {
// syncAlreadyInProgress flag is set by AbstractThreadedSyncAdapter. The sync adapter
// has no way of knowing that a sync error occured. So we DO retry if the error is
// syncAlreadyInProgress.
@@ -1720,10 +1714,9 @@
Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified "
+ operation);
}
- } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)
- && !syncResult.syncAlreadyInProgress) {
+ } else if (operation.isUpload() && !syncResult.syncAlreadyInProgress) {
// If this was an upward sync then schedule a two-way sync immediately.
- operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD);
+ operation.enableTwoWaySync();
if (isLoggable) {
Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
+ "encountered an error: " + operation);
@@ -3326,7 +3319,7 @@
List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (op.isPeriodic && op.target.matchesSpec(target)
- && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) {
+ && op.areExtrasEqual(extras, /*includeSyncSettings=*/ true)) {
maybeUpdateSyncPeriodH(op, pollFrequencyMillis, flexMillis);
return;
}
@@ -3408,7 +3401,7 @@
List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (op.isPeriodic && op.target.matchesSpec(target)
- && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) {
+ && op.areExtrasEqual(extras, /*includeSyncSettings=*/ true)) {
removePeriodicSyncInternalH(op, why);
}
}
@@ -3559,16 +3552,18 @@
activeSyncContext.mIsLinkedToDeath = true;
syncAdapter.linkToDeath(activeSyncContext, 0);
- mLogger.log("Sync start: account=" + syncOperation.target.account,
- " authority=", syncOperation.target.provider,
- " reason=", SyncOperation.reasonToString(null, syncOperation.reason),
- " extras=", SyncOperation.extrasToString(syncOperation.extras),
- " adapter=", activeSyncContext.mSyncAdapter);
+ if (mLogger.enabled()) {
+ mLogger.log("Sync start: account=" + syncOperation.target.account,
+ " authority=", syncOperation.target.provider,
+ " reason=", SyncOperation.reasonToString(null, syncOperation.reason),
+ " extras=", syncOperation.getExtrasAsString(),
+ " adapter=", activeSyncContext.mSyncAdapter);
+ }
activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter);
activeSyncContext.mSyncAdapter
.startSync(activeSyncContext, syncOperation.target.provider,
- syncOperation.target.account, syncOperation.extras);
+ syncOperation.target.account, syncOperation.getClonedExtras());
mLogger.log("Sync is running now...");
} catch (RemoteException remoteExc) {
@@ -3602,9 +3597,8 @@
continue;
}
if (extras != null &&
- !syncExtrasEquals(activeSyncContext.mSyncOperation.extras,
- extras,
- false /* no config settings */)) {
+ !activeSyncContext.mSyncOperation.areExtrasEqual(extras,
+ /*includeSyncSettings=*/ false)) {
continue;
}
SyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false,
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 2abc2e6..09b7828 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -74,7 +74,14 @@
/** Where this sync was initiated. */
public final int syncSource;
public final boolean allowParallelSyncs;
- public final Bundle extras;
+
+ /**
+ * Sync extras. Note, DO NOT modify this bundle directly. When changing the content, always
+ * create a copy, update it, set it in this field. This is to avoid concurrent modifications
+ * when other threads are reading it.
+ */
+ private volatile Bundle mImmutableExtras;
+
public final boolean isPeriodic;
/** jobId of the periodic SyncOperation that initiated this one */
public final int sourcePeriodicId;
@@ -118,20 +125,21 @@
public SyncOperation(SyncOperation op, long periodMillis, long flexMillis) {
this(op.target, op.owningUid, op.owningPackage, op.reason, op.syncSource,
- new Bundle(op.extras), op.allowParallelSyncs, op.isPeriodic, op.sourcePeriodicId,
+ op.mImmutableExtras, op.allowParallelSyncs, op.isPeriodic, op.sourcePeriodicId,
periodMillis, flexMillis, ContentResolver.SYNC_EXEMPTION_NONE);
}
public SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
- int reason, int source, Bundle extras, boolean allowParallelSyncs,
- boolean isPeriodic, int sourcePeriodicId, long periodMillis,
- long flexMillis, @SyncExemption int syncExemptionFlag) {
+ int reason, int source, Bundle extras,
+ boolean allowParallelSyncs,
+ boolean isPeriodic, int sourcePeriodicId, long periodMillis,
+ long flexMillis, @SyncExemption int syncExemptionFlag) {
this.target = info;
this.owningUid = owningUid;
this.owningPackage = owningPackage;
this.reason = reason;
this.syncSource = source;
- this.extras = new Bundle(extras);
+ this.mImmutableExtras = new Bundle(extras);
this.allowParallelSyncs = allowParallelSyncs;
this.isPeriodic = isPeriodic;
this.sourcePeriodicId = sourcePeriodicId;
@@ -148,7 +156,7 @@
return null;
}
SyncOperation op = new SyncOperation(target, owningUid, owningPackage, reason, syncSource,
- new Bundle(extras), allowParallelSyncs, false, jobId /* sourcePeriodicId */,
+ mImmutableExtras, allowParallelSyncs, false, jobId /* sourcePeriodicId */,
periodMillis, flexMillis, ContentResolver.SYNC_EXEMPTION_NONE);
return op;
}
@@ -160,7 +168,10 @@
reason = other.reason;
syncSource = other.syncSource;
allowParallelSyncs = other.allowParallelSyncs;
- extras = new Bundle(other.extras);
+
+ // Since we treat this field as immutable, it's okay to use a shallow copy here.
+ // No need to create a copy.
+ mImmutableExtras = other.mImmutableExtras;
wakeLockName = other.wakeLockName();
isPeriodic = other.isPeriodic;
sourcePeriodicId = other.sourcePeriodicId;
@@ -173,7 +184,8 @@
/**
* All fields are stored in a corresponding key in the persistable bundle.
*
- * {@link #extras} is a Bundle and can contain parcelable objects. But only the type Account
+ * {@link #mImmutableExtras} is a Bundle and can contain parcelable objects.
+ * But only the type Account
* is allowed {@link ContentResolver#validateSyncExtrasBundle(Bundle)} that can't be stored in
* a PersistableBundle. For every value of type Account with key 'key', we store a
* PersistableBundle containing account information at key 'ACCOUNT:key'. The Account object
@@ -188,7 +200,9 @@
PersistableBundle jobInfoExtras = new PersistableBundle();
PersistableBundle syncExtrasBundle = new PersistableBundle();
- for (String key: extras.keySet()) {
+
+ final Bundle extras = mImmutableExtras;
+ for (String key : extras.keySet()) {
Object value = extras.get(key);
if (value instanceof Account) {
Account account = (Account) value;
@@ -327,7 +341,7 @@
boolean matchesPeriodicOperation(SyncOperation other) {
return target.matchesSpec(other.target)
- && SyncManager.syncExtrasEquals(extras, other.extras, true)
+ && SyncManager.syncExtrasEquals(mImmutableExtras, other.mImmutableExtras, true)
&& periodMillis == other.periodMillis && flexMillis == other.flexMillis;
}
@@ -345,6 +359,7 @@
}
private String toKey() {
+ final Bundle extras = mImmutableExtras;
StringBuilder sb = new StringBuilder();
sb.append("provider: ").append(target.provider);
sb.append(" account {name=" + target.account.name
@@ -372,6 +387,7 @@
String dump(PackageManager pm, boolean shorter, SyncAdapterStateFetcher appStates,
boolean logSafe) {
+ final Bundle extras = mImmutableExtras;
StringBuilder sb = new StringBuilder();
sb.append("JobId=").append(jobId)
.append(" ")
@@ -468,33 +484,67 @@
}
boolean isInitialization() {
- return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
+ return mImmutableExtras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
}
boolean isExpedited() {
- return extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
+ return mImmutableExtras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
}
- boolean ignoreBackoff() {
- return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false);
+ boolean isUpload() {
+ return mImmutableExtras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
+ }
+
+ /**
+ * Disable SYNC_EXTRAS_UPLOAD, so it will be a two-way (normal) sync.
+ */
+ void enableTwoWaySync() {
+ removeExtra(ContentResolver.SYNC_EXTRAS_UPLOAD);
+ }
+
+ boolean hasIgnoreBackoff() {
+ return mImmutableExtras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false);
+ }
+
+ /**
+ * Disable SYNC_EXTRAS_IGNORE_BACKOFF.
+ *
+ * The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given
+ * request. Retries of the request will always honor the backoff, so clear the
+ * flag in case we retry this request.
+ */
+ void enableBackoff() {
+ removeExtra(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF);
+ }
+
+ boolean hasDoNotRetry() {
+ return mImmutableExtras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false);
}
boolean isNotAllowedOnMetered() {
- return extras.getBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, false);
+ return mImmutableExtras.getBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, false);
}
boolean isManual() {
- return extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
+ return mImmutableExtras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
}
boolean isIgnoreSettings() {
- return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
+ return mImmutableExtras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
+ }
+
+ boolean hasRequireCharging() {
+ return mImmutableExtras.getBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING, false);
}
boolean isAppStandbyExempted() {
return syncExemptionFlag != ContentResolver.SYNC_EXEMPTION_NONE;
}
+ boolean areExtrasEqual(Bundle other, boolean includeSyncSettings) {
+ return SyncManager.syncExtrasEquals(mImmutableExtras, other, includeSyncSettings);
+ }
+
static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
if (bundle == null) {
sb.append("null");
@@ -507,7 +557,7 @@
sb.append("]");
}
- static String extrasToString(Bundle bundle) {
+ private static String extrasToString(Bundle bundle) {
final StringBuilder sb = new StringBuilder();
extrasToStringBuilder(bundle, sb);
return sb.toString();
@@ -531,4 +581,25 @@
logArray[3] = target.account.name.hashCode();
return logArray;
}
+
+ /**
+ * Removes a sync extra. Note do not call it from multiple threads simultaneously.
+ */
+ private void removeExtra(String key) {
+ final Bundle b = mImmutableExtras;
+ if (!b.containsKey(key)) {
+ return;
+ }
+ final Bundle clone = new Bundle(b);
+ clone.remove(key);
+ mImmutableExtras = clone;
+ }
+
+ public Bundle getClonedExtras() {
+ return new Bundle(mImmutableExtras);
+ }
+
+ public String getExtrasAsString() {
+ return extrasToString(mImmutableExtras);
+ }
}
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index afdcda9..8c510b7 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -137,7 +137,7 @@
/**
* String names for the sync source types.
*
- * KEEP THIS AND {@link SyncStatusInfo#SOURCE_COUNT} IN SYNC.
+ * KEEP THIS AND {@link SyncStatusInfo}.SOURCE_COUNT IN SYNC.
*/
public static final String[] SOURCES = {
"OTHER",
@@ -1117,7 +1117,7 @@
Slog.v(TAG, "setActiveSync: account="
+ " auth=" + activeSyncContext.mSyncOperation.target
+ " src=" + activeSyncContext.mSyncOperation.syncSource
- + " extras=" + activeSyncContext.mSyncOperation.extras);
+ + " extras=" + activeSyncContext.mSyncOperation.getExtrasAsString());
}
final EndPoint info = activeSyncContext.mSyncOperation.target;
AuthorityInfo authorityInfo = getOrCreateAuthorityLocked(
@@ -1179,7 +1179,7 @@
item.eventTime = now;
item.source = op.syncSource;
item.reason = op.reason;
- item.extras = op.extras;
+ item.extras = op.getClonedExtras();
item.event = EVENT_START;
item.syncExemptionFlag = op.syncExemptionFlag;
mSyncHistory.add(0, item);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ad4e81b..fd86f1d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -8609,21 +8609,11 @@
@VisibleForTesting
boolean canUseManagedServices(String pkg, Integer userId, String requiredPermission) {
- boolean canUseManagedServices = !mActivityManager.isLowRamDevice()
- || mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_WATCH);
-
- for (String whitelisted : getContext().getResources().getStringArray(
- R.array.config_allowedManagedServicesOnLowRamDevices)) {
- if (whitelisted.equals(pkg)) {
- canUseManagedServices = true;
- break;
- }
- }
-
+ boolean canUseManagedServices = true;
if (requiredPermission != null) {
try {
if (mPackageManager.checkPermission(requiredPermission, pkg, userId)
- != PackageManager.PERMISSION_GRANTED) {
+ != PackageManager.PERMISSION_GRANTED) {
canUseManagedServices = false;
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index d75eb6d..f221285 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -62,6 +62,7 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.content.om.OverlayConfig;
import com.android.server.FgThread;
import com.android.server.IoThread;
import com.android.server.LocalServices;
@@ -248,7 +249,8 @@
IdmapManager im = new IdmapManager(mPackageManager);
mSettings = new OverlayManagerSettings();
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
- getDefaultOverlayPackages(), new OverlayChangeListener());
+ OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(),
+ new OverlayChangeListener());
mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
final IntentFilter packageFilter = new IntentFilter();
@@ -835,7 +837,7 @@
case "basecodepath":
case "state":
case "isenabled":
- case "isstatic":
+ case "ismutable":
case "priority":
case "category":
dumpState.setField(arg);
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 9623542..2493057 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -18,7 +18,7 @@
import static android.content.om.OverlayInfo.STATE_DISABLED;
import static android.content.om.OverlayInfo.STATE_ENABLED;
-import static android.content.om.OverlayInfo.STATE_ENABLED_STATIC;
+import static android.content.om.OverlayInfo.STATE_ENABLED_IMMUTABLE;
import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
import static android.content.om.OverlayInfo.STATE_NO_IDMAP;
import static android.content.om.OverlayInfo.STATE_OVERLAY_IS_BEING_REPLACED;
@@ -37,6 +37,7 @@
import android.util.ArraySet;
import android.util.Slog;
+import com.android.internal.content.om.OverlayConfig;
import com.android.internal.util.ArrayUtils;
import java.io.PrintWriter;
@@ -69,6 +70,7 @@
private final PackageManagerHelper mPackageManager;
private final IdmapManager mIdmapManager;
private final OverlayManagerSettings mSettings;
+ private final OverlayConfig mOverlayConfig;
private final String[] mDefaultOverlays;
private final OverlayChangeListener mListener;
@@ -83,7 +85,7 @@
* should either scrap the overlay manager's previous settings or merge the old
* settings with the new.
*/
- private static boolean mustReinitializeOverlay(@NonNull final PackageInfo theTruth,
+ private boolean mustReinitializeOverlay(@NonNull final PackageInfo theTruth,
@Nullable final OverlayInfo oldSettings) {
if (oldSettings == null) {
return true;
@@ -94,27 +96,35 @@
if (!Objects.equals(theTruth.targetOverlayableName, oldSettings.targetOverlayableName)) {
return true;
}
- if (theTruth.isStaticOverlayPackage() != oldSettings.isStatic) {
+
+ boolean isMutable = isPackageConfiguredMutable(theTruth.packageName);
+ if (isMutable != oldSettings.isMutable) {
return true;
}
- // a change in priority is only relevant for static RROs: specifically,
- // a regular RRO should not have its state reset only because a change
- // in priority
- if (theTruth.isStaticOverlayPackage()
- && theTruth.overlayPriority != oldSettings.priority) {
+
+ if (getPackageConfiguredPriority(theTruth.packageName) != oldSettings.priority) {
return true;
}
+
+ // If an immutable overlay changes its configured enabled state, reinitialize the overlay.
+ if (!isMutable && isPackageConfiguredEnabled(theTruth.packageName)
+ != oldSettings.isEnabled()) {
+ return true;
+ }
+
return false;
}
OverlayManagerServiceImpl(@NonNull final PackageManagerHelper packageManager,
@NonNull final IdmapManager idmapManager,
@NonNull final OverlayManagerSettings settings,
+ @NonNull final OverlayConfig overlayConfig,
@NonNull final String[] defaultOverlays,
@NonNull final OverlayChangeListener listener) {
mPackageManager = packageManager;
mIdmapManager = idmapManager;
mSettings = settings;
+ mOverlayConfig = overlayConfig;
mDefaultOverlays = defaultOverlays;
mListener = listener;
}
@@ -162,8 +172,9 @@
overlayPackage.overlayTarget,
overlayPackage.targetOverlayableName,
overlayPackage.applicationInfo.getBaseCodePath(),
- overlayPackage.isStaticOverlayPackage(),
- overlayPackage.overlayPriority,
+ isPackageConfiguredMutable(overlayPackage.packageName),
+ isPackageConfiguredEnabled(overlayPackage.packageName),
+ getPackageConfiguredPriority(overlayPackage.packageName),
overlayPackage.overlayCategory);
}
@@ -374,7 +385,9 @@
mSettings.init(packageName, userId, overlayPackage.overlayTarget,
overlayPackage.targetOverlayableName,
overlayPackage.applicationInfo.getBaseCodePath(),
- overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority,
+ isPackageConfiguredMutable(overlayPackage.packageName),
+ isPackageConfiguredEnabled(overlayPackage.packageName),
+ getPackageConfiguredPriority(overlayPackage.packageName),
overlayPackage.overlayCategory);
try {
if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
@@ -439,8 +452,10 @@
mListener.onOverlaysChanged(pkg.overlayTarget, userId);
}
mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName,
- pkg.applicationInfo.getBaseCodePath(), pkg.isStaticOverlayPackage(),
- pkg.overlayPriority, pkg.overlayCategory);
+ pkg.applicationInfo.getBaseCodePath(),
+ isPackageConfiguredMutable(pkg.packageName),
+ isPackageConfiguredEnabled(pkg.packageName),
+ getPackageConfiguredPriority(pkg.packageName), pkg.overlayCategory);
}
if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
@@ -492,13 +507,13 @@
return false;
}
- // Ignore static overlays.
- if (overlayPackage.isStaticOverlayPackage()) {
- return false;
- }
-
try {
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
+ if (!oi.isMutable) {
+ // Ignore immutable overlays.
+ return false;
+ }
+
boolean modified = mSettings.setEnabled(packageName, userId, enable);
modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
@@ -534,7 +549,8 @@
// Disable all other overlays.
allOverlays.remove(oi);
for (int i = 0; i < allOverlays.size(); i++) {
- final String disabledOverlayPackageName = allOverlays.get(i).packageName;
+ final OverlayInfo disabledInfo = allOverlays.get(i);
+ final String disabledOverlayPackageName = disabledInfo.packageName;
final PackageInfo disabledOverlayPackageInfo = mPackageManager.getPackageInfo(
disabledOverlayPackageName, userId);
if (disabledOverlayPackageInfo == null) {
@@ -542,8 +558,8 @@
continue;
}
- if (disabledOverlayPackageInfo.isStaticOverlayPackage()) {
- // Don't touch static overlays.
+ if (!disabledInfo.isMutable) {
+ // Don't touch immutable overlays.
continue;
}
if (withinCategory && !Objects.equals(disabledOverlayPackageInfo.overlayCategory,
@@ -570,12 +586,16 @@
}
}
- private boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) {
- final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
- if (overlayPackage == null || overlayPackage.isStaticOverlayPackage()) {
- return false;
- }
- return true;
+ private boolean isPackageConfiguredMutable(@NonNull final String packageName) {
+ return mOverlayConfig.isMutable(packageName);
+ }
+
+ private int getPackageConfiguredPriority(@NonNull final String packageName) {
+ return mOverlayConfig.getPriority(packageName);
+ }
+
+ private boolean isPackageConfiguredEnabled(@NonNull final String packageName) {
+ return mOverlayConfig.isEnabled(packageName);
}
boolean setPriority(@NonNull final String packageName,
@@ -585,7 +605,7 @@
+ newParentPackageName + " userId=" + userId);
}
- if (!isPackageUpdatableOverlay(packageName, userId)) {
+ if (!isPackageConfiguredMutable(packageName)) {
return false;
}
@@ -605,7 +625,7 @@
Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
}
- if (!isPackageUpdatableOverlay(packageName, userId)) {
+ if (!isPackageConfiguredMutable(packageName)) {
return false;
}
@@ -625,7 +645,7 @@
Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
}
- if (!isPackageUpdatableOverlay(packageName, userId)) {
+ if (!isPackageConfiguredMutable(packageName)) {
return false;
}
@@ -682,10 +702,10 @@
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(overlayPackageName,
userId);
- // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
+ // Immutable RROs targeting to "android", ie framework-res.apk, are handled by native layers.
if (targetPackage != null && overlayPackage != null
&& !("android".equals(targetPackageName)
- && overlayPackage.isStaticOverlayPackage())) {
+ && !isPackageConfiguredMutable(overlayPackageName))) {
mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
}
@@ -737,10 +757,6 @@
return STATE_NO_IDMAP;
}
- if (overlayPackage.isStaticOverlayPackage()) {
- return STATE_ENABLED_STATIC;
- }
-
final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId);
return enabled ? STATE_ENABLED : STATE_DISABLED;
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index b7346d4..6bccdfc 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -67,32 +67,27 @@
private final ArrayList<SettingsItem> mItems = new ArrayList<>();
void init(@NonNull final String packageName, final int userId,
- @NonNull final String targetPackageName, @Nullable final String targetOverlayableName,
- @NonNull final String baseCodePath, boolean isStatic, int priority,
+ @NonNull final String targetPackageName, @Nullable final String targetOverlayableName,
+ @NonNull final String baseCodePath, boolean isMutable, boolean isEnabled, int priority,
@Nullable String overlayCategory) {
remove(packageName, userId);
final SettingsItem item =
new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName,
- baseCodePath, isStatic, priority, overlayCategory);
- if (isStatic) {
- // All static overlays are always enabled.
- item.setEnabled(true);
+ baseCodePath, OverlayInfo.STATE_UNKNOWN, isEnabled, isMutable, priority,
+ overlayCategory);
- int i;
- for (i = mItems.size() - 1; i >= 0; i--) {
- SettingsItem parentItem = mItems.get(i);
- if (parentItem.mIsStatic && parentItem.mPriority <= priority) {
- break;
- }
+ int i;
+ for (i = mItems.size() - 1; i >= 0; i--) {
+ SettingsItem parentItem = mItems.get(i);
+ if (parentItem.mPriority <= priority) {
+ break;
}
- int pos = i + 1;
- if (pos == mItems.size()) {
- mItems.add(item);
- } else {
- mItems.add(pos, item);
- }
- } else {
+ }
+ int pos = i + 1;
+ if (pos == mItems.size()) {
mItems.add(item);
+ } else {
+ mItems.add(pos, item);
}
}
@@ -182,19 +177,19 @@
List<OverlayInfo> getOverlaysForTarget(@NonNull final String targetPackageName,
final int userId) {
- // Static RROs targeting "android" are loaded from AssetManager, and so they should be
+ // Immutable RROs targeting "android" are loaded from AssetManager, and so they should be
// ignored in OverlayManagerService.
return selectWhereTarget(targetPackageName, userId)
- .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName())))
+ .filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName()))
.map(SettingsItem::getOverlayInfo)
.collect(Collectors.toList());
}
ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
- // Static RROs targeting "android" are loaded from AssetManager, and so they should be
+ // Immutable RROs targeting "android" are loaded from AssetManager, and so they should be
// ignored in OverlayManagerService.
return selectWhereUser(userId)
- .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName())))
+ .filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName()))
.map(SettingsItem::getOverlayInfo)
.collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new,
Collectors.toList()));
@@ -320,7 +315,7 @@
pw.println("mBaseCodePath..........: " + item.getBaseCodePath());
pw.println("mState.................: " + OverlayInfo.stateToString(item.getState()));
pw.println("mIsEnabled.............: " + item.isEnabled());
- pw.println("mIsStatic..............: " + item.isStatic());
+ pw.println("mIsMutable.............: " + item.isMutable());
pw.println("mPriority..............: " + item.mPriority);
pw.println("mCategory..............: " + item.mCategory);
@@ -352,8 +347,8 @@
case "isenabled":
pw.println(item.mIsEnabled);
break;
- case "isstatic":
- pw.println(item.mIsStatic);
+ case "ismutable":
+ pw.println(item.mIsMutable);
break;
case "priority":
pw.println(item.mPriority);
@@ -446,7 +441,7 @@
final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY);
return new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName,
- baseCodePath, state, isEnabled, isStatic, priority, category);
+ baseCodePath, state, isEnabled, !isStatic, priority, category);
}
public static void persist(@NonNull final ArrayList<SettingsItem> table,
@@ -478,7 +473,7 @@
XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.mBaseCodePath);
XmlUtils.writeIntAttribute(xml, ATTR_STATE, item.mState);
XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.mIsEnabled);
- XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, item.mIsStatic);
+ XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, !item.mIsMutable);
XmlUtils.writeIntAttribute(xml, ATTR_PRIORITY, item.mPriority);
XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory);
xml.endTag(null, TAG_ITEM);
@@ -494,36 +489,28 @@
private @OverlayInfo.State int mState;
private boolean mIsEnabled;
private OverlayInfo mCache;
- private boolean mIsStatic;
+ private boolean mIsMutable;
private int mPriority;
private String mCategory;
SettingsItem(@NonNull final String packageName, final int userId,
@NonNull final String targetPackageName,
@Nullable final String targetOverlayableName, @NonNull final String baseCodePath,
- final @OverlayInfo.State int state, final boolean isEnabled, final boolean isStatic,
- final int priority, @Nullable String category) {
+ final @OverlayInfo.State int state, final boolean isEnabled,
+ final boolean isMutable, final int priority, @Nullable String category) {
mPackageName = packageName;
mUserId = userId;
mTargetPackageName = targetPackageName;
mTargetOverlayableName = targetOverlayableName;
mBaseCodePath = baseCodePath;
mState = state;
- mIsEnabled = isEnabled || isStatic;
+ mIsEnabled = isEnabled;
mCategory = category;
mCache = null;
- mIsStatic = isStatic;
+ mIsMutable = isMutable;
mPriority = priority;
}
- SettingsItem(@NonNull final String packageName, final int userId,
- @NonNull final String targetPackageName,
- @Nullable final String targetOverlayableName, @NonNull final String baseCodePath,
- final boolean isStatic, final int priority, @Nullable String category) {
- this(packageName, userId, targetPackageName, targetOverlayableName, baseCodePath,
- OverlayInfo.STATE_UNKNOWN, false, isStatic, priority, category);
- }
-
private String getTargetPackageName() {
return mTargetPackageName;
}
@@ -567,7 +554,7 @@
}
private boolean setEnabled(boolean enable) {
- if (mIsStatic) {
+ if (!mIsMutable) {
return false;
}
@@ -591,7 +578,7 @@
private OverlayInfo getOverlayInfo() {
if (mCache == null) {
mCache = new OverlayInfo(mPackageName, mTargetPackageName, mTargetOverlayableName,
- mCategory, mBaseCodePath, mState, mUserId, mPriority, mIsStatic);
+ mCategory, mBaseCodePath, mState, mUserId, mPriority, mIsMutable);
}
return mCache;
}
@@ -600,8 +587,8 @@
mCache = null;
}
- private boolean isStatic() {
- return mIsStatic;
+ private boolean isMutable() {
+ return mIsMutable;
}
private int getPriority() {
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index eb43275..bf99bd6 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -181,7 +181,7 @@
private void printListOverlay(PrintWriter out, OverlayInfo oi) {
String status;
switch (oi.state) {
- case OverlayInfo.STATE_ENABLED_STATIC:
+ case OverlayInfo.STATE_ENABLED_IMMUTABLE:
case OverlayInfo.STATE_ENABLED:
status = "[x]";
break;
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 0fb889c..a1250cb 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -69,7 +69,6 @@
// Logs all filtering instead of enforcing
private static final boolean DEBUG_ALLOW_ALL = false;
private static final boolean DEBUG_LOGGING = false;
- private static final boolean FEATURE_ENABLED_BY_DEFAULT = true;
/**
* This contains a list of app UIDs that are implicitly queryable because another app explicitly
@@ -135,7 +134,8 @@
private static class FeatureConfigImpl implements FeatureConfig {
private static final String FILTERING_ENABLED_NAME = "package_query_filtering_enabled";
private final PackageManagerService.Injector mInjector;
- private volatile boolean mFeatureEnabled = FEATURE_ENABLED_BY_DEFAULT;
+ private volatile boolean mFeatureEnabled =
+ PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT;
private FeatureConfigImpl(PackageManagerService.Injector injector) {
mInjector = injector;
@@ -145,14 +145,14 @@
public void onSystemReady() {
mFeatureEnabled = DeviceConfig.getBoolean(
NAMESPACE_PACKAGE_MANAGER_SERVICE, FILTERING_ENABLED_NAME,
- FEATURE_ENABLED_BY_DEFAULT);
+ PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT);
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(),
properties -> {
if (properties.getKeyset().contains(FILTERING_ENABLED_NAME)) {
synchronized (FeatureConfigImpl.this) {
mFeatureEnabled = properties.getBoolean(FILTERING_ENABLED_NAME,
- FEATURE_ENABLED_BY_DEFAULT);
+ PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT);
}
}
});
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 13bd7e5..ea48395 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -32,6 +32,7 @@
import android.content.pm.InstantAppResolveInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
import android.content.pm.PackageUserState;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
@@ -229,9 +230,11 @@
}
@Nullable
- List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags, int userId) {
+ List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags,
+ @PrivateResolveFlags int privateResolveFlags, int userId) {
synchronized (mLock) {
- return mActivities.queryIntent(intent, resolvedType, flags, userId);
+ return mActivities.queryIntent(
+ intent, resolvedType, flags, privateResolveFlags, userId);
}
}
@@ -368,7 +371,7 @@
@Nullable
List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, int flags, int userId) {
synchronized (mLock) {
- return mReceivers.queryIntent(intent, resolvedType, flags, userId);
+ return mReceivers.queryIntent(intent, resolvedType, flags, 0, userId);
}
}
@@ -1154,11 +1157,12 @@
}
List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
- int userId) {
+ int privateResolveFlags, int userId) {
if (!sUserManager.exists(userId)) {
return null;
}
mFlags = flags;
+ mPrivateResolveFlags = privateResolveFlags;
return super.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
userId);
@@ -1388,6 +1392,11 @@
}
return null;
}
+ final boolean matchNonBrowserOnly =
+ (mPrivateResolveFlags & PackageManagerInternal.RESOLVE_NON_BROWSER_ONLY) != 0;
+ if (matchNonBrowserOnly && info.handleAllWebDataURI()) {
+ return null;
+ }
final ResolveInfo res = new ResolveInfo();
res.activityInfo = ai;
if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
@@ -1465,6 +1474,7 @@
private final ArrayMap<ComponentName, ParsedActivity> mActivities =
new ArrayMap<>();
private int mFlags;
+ private int mPrivateResolveFlags;
}
// Both receivers and activities share a class, but point to different get methods
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ad70345..7cfe499 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -179,12 +179,15 @@
import android.content.pm.PackageManager.ModuleInfoFlags;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackageListObserver;
+import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser.ParseFlags;
import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
+import android.content.pm.PackagePartitions;
+import android.content.pm.PackagePartitions.SystemPartition;
import android.content.pm.PackageStats;
import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
@@ -305,6 +308,7 @@
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
+import com.android.internal.content.om.OverlayConfig;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.SomeArgs;
import com.android.internal.os.Zygote;
@@ -792,22 +796,12 @@
* specificity (the more generic, the earlier in the list a partition appears).
*/
@VisibleForTesting(visibility = Visibility.PRIVATE)
- public static final List<SystemPartition> SYSTEM_PARTITIONS = Collections.unmodifiableList(
- Arrays.asList(
- new SystemPartition(Environment.getRootDirectory(), 0 /* scanFlag */,
- false /* hasOverlays */),
- new SystemPartition(Environment.getVendorDirectory(), SCAN_AS_VENDOR,
- true /* hasOverlays */),
- new SystemPartition(Environment.getOdmDirectory(), SCAN_AS_ODM,
- true /* hasOverlays */),
- new SystemPartition(Environment.getOemDirectory(), SCAN_AS_OEM,
- true /* hasOverlays */),
- new SystemPartition(Environment.getProductDirectory(), SCAN_AS_PRODUCT,
- true /* hasOverlays */),
- new SystemPartition(Environment.getSystemExtDirectory(), SCAN_AS_SYSTEM_EXT,
- true /* hasOverlays */)));
+ public static final List<ScanPartition> SYSTEM_PARTITIONS = Collections.unmodifiableList(
+ PackagePartitions.getOrderedPartitions(ScanPartition::new));
- private final List<SystemPartition> mDirsToScanAsSystem;
+ private final List<ScanPartition> mDirsToScanAsSystem;
+
+ private final OverlayConfig mOverlayConfig;
/**
* Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
@@ -2588,66 +2582,44 @@
}
}
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- public static class SystemPartition {
- public final File folder;
+ @VisibleForTesting
+ public static class ScanPartition extends SystemPartition {
+ @ScanFlags
public final int scanFlag;
- public final File appFolder;
- @Nullable
- public final File privAppFolder;
- @Nullable
- public final File overlayFolder;
-
- private static boolean shouldScanPrivApps(@ScanFlags int scanFlags) {
- if ((scanFlags & SCAN_AS_OEM) != 0) {
- return false;
- }
- if (scanFlags == 0) { // /system partition
- return true;
- }
- if ((scanFlags
- & (SCAN_AS_VENDOR | SCAN_AS_ODM | SCAN_AS_PRODUCT | SCAN_AS_SYSTEM_EXT)) != 0) {
- return true;
- }
- if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
- return true;
- }
- return false;
+ public ScanPartition(@NonNull SystemPartition partition) {
+ super(partition);
+ scanFlag = scanFlagForPartition(partition);
}
- private SystemPartition(File folder, int scanFlag, boolean hasOverlays) {
- this.folder = folder;
- this.scanFlag = scanFlag;
- this.appFolder = toCanonical(new File(folder, "app"));
- this.privAppFolder = shouldScanPrivApps(scanFlag)
- ? toCanonical(new File(folder, "priv-app"))
- : null;
- this.overlayFolder = hasOverlays ? toCanonical(new File(folder, "overlay")) : null;
+ /**
+ * Creates a partition containing the same folders as the original partition but with a
+ * different root folder. The new partition will include the scan flags of the original
+ * partition along with any specified additional scan flags.
+ */
+ public ScanPartition(@NonNull File folder, @NonNull ScanPartition original,
+ @ScanFlags int additionalScanFlag) {
+ super(folder, original);
+ this.scanFlag = original.scanFlag | additionalScanFlag;
}
- public boolean containsPrivApp(File scanFile) {
- return FileUtils.contains(privAppFolder, scanFile);
- }
-
- public boolean containsApp(File scanFile) {
- return FileUtils.contains(appFolder, scanFile);
- }
-
- public boolean containsPath(String path) {
- return path.startsWith(folder.getPath() + "/");
- }
-
- public boolean containsPrivPath(String path) {
- return privAppFolder != null && path.startsWith(privAppFolder.getPath() + "/");
- }
-
- private static File toCanonical(File dir) {
- try {
- return dir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- return dir;
+ private static int scanFlagForPartition(PackagePartitions.SystemPartition partition) {
+ switch (partition.type) {
+ case PackagePartitions.PARTITION_SYSTEM:
+ return 0;
+ case PackagePartitions.PARTITION_VENDOR:
+ return SCAN_AS_VENDOR;
+ case PackagePartitions.PARTITION_ODM:
+ return SCAN_AS_ODM;
+ case PackagePartitions.PARTITION_OEM:
+ return SCAN_AS_OEM;
+ case PackagePartitions.PARTITION_PRODUCT:
+ return SCAN_AS_PRODUCT;
+ case PackagePartitions.PARTITION_SYSTEM_EXT:
+ return SCAN_AS_SYSTEM_EXT;
+ default:
+ throw new IllegalStateException("Unable to determine scan flag for "
+ + partition.folder);
}
}
}
@@ -2751,7 +2723,7 @@
mDirsToScanAsSystem = new ArrayList<>();
mDirsToScanAsSystem.addAll(SYSTEM_PARTITIONS);
mDirsToScanAsSystem.addAll(mApexManager.getActiveApexInfos().stream()
- .map(ai -> resolveApexToSystemPartition(ai))
+ .map(PackageManagerService::resolveApexToScanPartition)
.filter(Objects::nonNull).collect(Collectors.toList()));
Slog.d(TAG,
"Directories scanned as system partitions: [" + mDirsToScanAsSystem.stream().map(
@@ -2900,11 +2872,11 @@
// For security and version matching reason, only consider overlay packages if they
// reside in the right directory.
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
- final SystemPartition partition = mDirsToScanAsSystem.get(i);
- if (partition.overlayFolder == null) {
+ final ScanPartition partition = mDirsToScanAsSystem.get(i);
+ if (partition.getOverlayFolder() == null) {
continue;
}
- scanDirTracedLI(partition.overlayFolder, systemParseFlags,
+ scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
@@ -2917,17 +2889,20 @@
"Failed to load frameworks package; check log for warnings");
}
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
- final SystemPartition partition = mDirsToScanAsSystem.get(i);
- if (partition.privAppFolder != null) {
- scanDirTracedLI(partition.privAppFolder, systemParseFlags,
+ final ScanPartition partition = mDirsToScanAsSystem.get(i);
+ if (partition.getPrivAppFolder() != null) {
+ scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
packageParser, executorService);
}
- scanDirTracedLI(partition.appFolder, systemParseFlags,
+ scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
+ // Parse overlay configuration files to set default enable state, mutability, and
+ // priority of system overlays.
+ mOverlayConfig = OverlayConfig.initializeSystemInstance(mPmInternal::forEachPackage);
// Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
@@ -3105,7 +3080,7 @@
@ParseFlags int reparseFlags = 0;
@ScanFlags int rescanFlags = 0;
for (int i1 = 0, size = mDirsToScanAsSystem.size(); i1 < size; i1++) {
- SystemPartition partition = mDirsToScanAsSystem.get(i1);
+ final ScanPartition partition = mDirsToScanAsSystem.get(i1);
if (partition.containsPrivApp(scanFile)) {
reparseFlags = systemParseFlags;
rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
@@ -6128,8 +6103,8 @@
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
- return resolveIntentInternal(intent, resolvedType, flags, userId, false,
- Binder.getCallingUid());
+ return resolveIntentInternal(intent, resolvedType, flags, 0 /*privateResolveFlags*/,
+ userId, false, Binder.getCallingUid());
}
/**
@@ -6137,8 +6112,9 @@
* However, if {@code resolveForStart} is {@code true}, all instant apps are visible
* since we need to allow the system to start any installed application.
*/
- private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
- int flags, int userId, boolean resolveForStart, int filterCallingUid) {
+ private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, int flags,
+ @PrivateResolveFlags int privateResolveFlags, int userId, boolean resolveForStart,
+ int filterCallingUid) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
@@ -6150,11 +6126,13 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
- flags, filterCallingUid, userId, resolveForStart, true /*allowDynamicSplits*/);
+ flags, privateResolveFlags, filterCallingUid, userId, resolveForStart,
+ true /*allowDynamicSplits*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
final ResolveInfo bestChoice =
- chooseBestActivity(intent, resolvedType, flags, query, userId);
+ chooseBestActivity(
+ intent, resolvedType, flags, privateResolveFlags, query, userId);
return bestChoice;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -6312,7 +6290,7 @@
}
private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
- int flags, List<ResolveInfo> query, int userId) {
+ int flags, int privateResolveFlags, List<ResolveInfo> query, int userId) {
if (query != null) {
final int N = query.size();
if (N == 1) {
@@ -6354,6 +6332,10 @@
}
}
}
+ if ((privateResolveFlags
+ & PackageManagerInternal.RESOLVE_NON_RESOLVER_ONLY) != 0) {
+ return null;
+ }
ri = new ResolveInfo(mResolveInfo);
ri.activityInfo = new ActivityInfo(ri.activityInfo);
ri.activityInfo.labelRes = ResolverActivity.getLabelRes(intent.getAction());
@@ -6767,13 +6749,13 @@
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int userId) {
return queryIntentActivitiesInternal(
- intent, resolvedType, flags, Binder.getCallingUid(), userId,
- false /*resolveForStart*/, true /*allowDynamicSplits*/);
+ intent, resolvedType, flags, 0 /*privateResolveFlags*/, Binder.getCallingUid(),
+ userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
}
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
- String resolvedType, int flags, int filterCallingUid, int userId,
- boolean resolveForStart, boolean allowDynamicSplits) {
+ String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags,
+ int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
@@ -6858,7 +6840,7 @@
// Check for results in the current profile.
result = filterIfNotSystemUser(mComponentResolver.queryActivities(
- intent, resolvedType, flags, userId), userId);
+ intent, resolvedType, flags, privateResolveFlags, userId), userId);
addInstant = isInstantAppResolutionAllowed(intent, result, userId,
false /*skipPackageCheck*/);
// Check for cross profile results.
@@ -6957,7 +6939,7 @@
| PackageManager.GET_RESOLVED_FILTER
| PackageManager.MATCH_INSTANT
| PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY,
- userId);
+ 0, userId);
for (int i = instantApps.size() - 1; i >= 0; --i) {
final ResolveInfo info = instantApps.get(i);
final String packageName = info.activityInfo.packageName;
@@ -7061,7 +7043,7 @@
return null;
}
List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
- resolvedType, flags, parentUserId);
+ resolvedType, flags, 0, parentUserId);
if (resultTargetUser == null || resultTargetUser.isEmpty()) {
return null;
@@ -7246,8 +7228,9 @@
failureActivityIntent.setPackage(packageName);
// IMPORTANT: disallow dynamic splits to avoid an infinite loop
final List<ResolveInfo> result = queryIntentActivitiesInternal(
- failureActivityIntent, null /*resolvedType*/, 0 /*flags*/, filterCallingUid, userId,
- false /*resolveForStart*/, false /*allowDynamicSplits*/);
+ failureActivityIntent, null /*resolvedType*/, 0 /*flags*/,
+ 0 /*privateResolveFlags*/, filterCallingUid, userId, false /*resolveForStart*/,
+ false /*allowDynamicSplits*/);
final int NR = result.size();
if (NR > 0) {
for (int i = 0; i < NR; i++) {
@@ -7502,7 +7485,7 @@
String resolvedType, int flags, int sourceUserId) {
int targetUserId = filter.getTargetUserId();
List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
- resolvedType, flags, targetUserId);
+ resolvedType, flags, 0, targetUserId);
if (resultTargetUser != null && isUserEnabled(targetUserId)) {
// If all the matches in the target profile are suspended, return null.
for (int i = resultTargetUser.size() - 1; i >= 0; i--) {
@@ -11570,50 +11553,17 @@
// We are scanning a system overlay. This can be the first scan of the
// system/vendor/oem partition, or an update to the system overlay.
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
- // This must be an update to a system overlay.
- final PackageSetting previousPkg = assertNotNull(
- mSettings.getPackageLPr(pkg.getPackageName()),
- "previous package state not present");
-
- // previousPkg.pkg may be null: the package will be not be scanned if the
- // package manager knows there is a newer version on /data.
- // TODO[b/79435695]: Find a better way to keep track of the "static"
- // property for RROs instead of having to parse packages on /system
- AndroidPackage ppkg = previousPkg.pkg;
- if (ppkg == null) {
- try {
- final PackageParser pp = new PackageParser();
- // TODO(b/135203078): Do we really need to parse here? Maybe use
- // a shortened path?
- ppkg = pp.parseParsedPackage(previousPkg.codePath,
- parseFlags | PackageParser.PARSE_IS_SYSTEM_DIR,
- false)
- .hideAsFinal();
- } catch (PackageParserException e) {
- Slog.w(TAG, "failed to parse " + previousPkg.codePath, e);
- }
- }
-
- // Static overlays cannot be updated.
- if (ppkg != null && ppkg.isOverlayIsStatic()) {
+ // This must be an update to a system overlay. Immutable overlays cannot be
+ // upgraded.
+ Objects.requireNonNull(mOverlayConfig,
+ "Parsing non-system dir before overlay configs are initialized");
+ if (!mOverlayConfig.isMutable(pkg.getPackageName())) {
throw new PackageManagerException("Overlay "
+ pkg.getPackageName()
+ " is static and cannot be upgraded.");
- // Non-static overlays cannot be converted to static overlays.
- } else if (pkg.isOverlayIsStatic()) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
- + " cannot be upgraded into a static overlay.");
}
}
} else {
- // The overlay is a non-system overlay. Non-system overlays cannot be static.
- if (pkg.isOverlayIsStatic()) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
- + " is static but not pre-installed.");
- }
-
// A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
// signed with the platform certificate. Check this in increasing order of
// computational cost.
@@ -17971,14 +17921,13 @@
}
}
- private static @Nullable SystemPartition resolveApexToSystemPartition(
+ private static @Nullable ScanPartition resolveApexToScanPartition(
ApexManager.ActiveApexInfo apexInfo) {
for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
- SystemPartition sp = SYSTEM_PARTITIONS.get(i);
+ ScanPartition sp = SYSTEM_PARTITIONS.get(i);
if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
sp.folder.getAbsolutePath())) {
- return new SystemPartition(apexInfo.apexDirectory,
- sp.scanFlag | SCAN_AS_APK_IN_APEX, false /* hasOverlays */);
+ return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX);
}
}
return null;
@@ -18079,7 +18028,7 @@
| PackageParser.PARSE_IS_SYSTEM_DIR;
@ScanFlags int scanFlags = SCAN_AS_SYSTEM;
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
- SystemPartition partition = mDirsToScanAsSystem.get(i);
+ ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.containsPath(codePathString)) {
scanFlags |= partition.scanFlag;
if (partition.containsPrivPath(codePathString)) {
@@ -23324,7 +23273,7 @@
public List<ResolveInfo> queryIntentActivities(
Intent intent, String resolvedType, int flags, int filterCallingUid, int userId) {
return PackageManagerService.this
- .queryIntentActivitiesInternal(intent, resolvedType, flags, filterCallingUid,
+ .queryIntentActivitiesInternal(intent, resolvedType, flags, 0, filterCallingUid,
userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
}
@@ -23610,9 +23559,11 @@
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
- int flags, int userId, boolean resolveForStart, int filterCallingUid) {
+ int flags, int privateResolveFlags, int userId, boolean resolveForStart,
+ int filterCallingUid) {
return resolveIntentInternal(
- intent, resolvedType, flags, userId, resolveForStart, filterCallingUid);
+ intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,
+ filterCallingUid);
}
@Override
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 81c7471..97f9548 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -13,6 +13,9 @@
"name": "AppEnumerationTests"
},
{
+ "name": "CtsMatchFlagTestCases"
+ },
+ {
"name": "FrameworksServicesTests",
"options": [
{
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index c13cb38..ec8e1a0 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -39,7 +39,9 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PermissionInfo;
+import android.content.pm.parsing.AndroidPackage;
import android.os.Build;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -50,15 +52,14 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LongSparseLongArray;
+import android.util.Pair;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.infra.AndroidFuture;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IntPair;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
@@ -69,7 +70,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
import java.util.concurrent.ExecutionException;
/**
@@ -100,7 +100,7 @@
* scheduled for a package/user.
*/
@GuardedBy("mLock")
- private final ArraySet<Integer> mIsPackageSyncsScheduled = new ArraySet<>();
+ private final ArraySet<Pair<String, Integer>> mIsPackageSyncsScheduled = new ArraySet<>();
public PermissionPolicyService(@NonNull Context context) {
super(context);
@@ -125,8 +125,10 @@
@Override
public void onPackageChanged(String packageName, int uid) {
- if (isStarted(UserHandle.getUserId(uid))) {
- synchronizePackagePermissionsAndAppOpsForUser(uid);
+ final int userId = UserHandle.getUserId(uid);
+
+ if (isStarted(userId)) {
+ synchronizePackagePermissionsAndAppOpsForUser(packageName, userId);
}
}
@@ -137,21 +139,12 @@
});
permManagerInternal.addOnRuntimePermissionStateChangedListener(
- (packageName, userId) -> {
- int uid;
- try {
- uid = getContext().getPackageManager().getPackageUidAsUser(packageName, 0,
- userId);
- } catch (NameNotFoundException e) {
- Slog.e(LOG_TAG, "Cannot synchronize changed package " + packageName, e);
- return;
- }
- synchronizeUidPermissionsAndAppOpsAsync(uid);
- });
+ this::synchronizePackagePermissionsAndAppOpsAsyncForUser);
mAppOpsCallback = new IAppOpsCallback.Stub() {
public void opChanged(int op, int uid, String packageName) {
- synchronizeUidPermissionsAndAppOpsAsync(uid);
+ synchronizePackagePermissionsAndAppOpsAsyncForUser(packageName,
+ UserHandle.getUserId(uid));
}
};
@@ -201,17 +194,19 @@
return AppOpsManager.opToSwitch(op);
}
- private void synchronizeUidPermissionsAndAppOpsAsync(int uid) {
- if (isStarted(UserHandle.getUserId(uid))) {
+ private void synchronizePackagePermissionsAndAppOpsAsyncForUser(@NonNull String packageName,
+ @UserIdInt int changedUserId) {
+ if (isStarted(changedUserId)) {
synchronized (mLock) {
- if (mIsPackageSyncsScheduled.add(uid)) {
+ if (mIsPackageSyncsScheduled.add(new Pair<>(packageName, changedUserId))) {
FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
PermissionPolicyService
::synchronizePackagePermissionsAndAppOpsForUser,
- this, uid));
+ this, packageName, changedUserId));
} else {
if (DEBUG) {
- Slog.v(LOG_TAG, "sync for " + uid + " already scheduled");
+ Slog.v(LOG_TAG, "sync for " + packageName + "/" + changedUserId
+ + " already scheduled");
}
}
}
@@ -340,20 +335,39 @@
/**
* Synchronize a single package.
*/
- private void synchronizePackagePermissionsAndAppOpsForUser(int uid) {
+ private void synchronizePackagePermissionsAndAppOpsForUser(@NonNull String packageName,
+ @UserIdInt int userId) {
synchronized (mLock) {
- mIsPackageSyncsScheduled.remove(uid);
+ mIsPackageSyncsScheduled.remove(new Pair<>(packageName, userId));
}
if (DEBUG) {
Slog.v(LOG_TAG,
- "synchronizePackagePermissionsAndAppOpsForUser(" + uid + ")");
+ "synchronizePackagePermissionsAndAppOpsForUser(" + packageName + ", "
+ + userId + ")");
}
+ final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ final PackageInfo pkg = packageManagerInternal.getPackageInfo(packageName, 0,
+ Process.SYSTEM_UID, userId);
+ if (pkg == null) {
+ return;
+ }
final PermissionToOpSynchroniser synchroniser = new PermissionToOpSynchroniser(
- getUserContext(getContext(), UserHandle.getUserHandleForUid(uid)));
- synchroniser.addUid(uid);
- synchroniser.syncUids();
+ getUserContext(getContext(), UserHandle.of(userId)));
+ synchroniser.addPackage(pkg.packageName);
+ final String[] sharedPkgNames = packageManagerInternal.getSharedUserPackagesForPackage(
+ pkg.packageName, userId);
+
+ for (String sharedPkgName : sharedPkgNames) {
+ final AndroidPackage sharedPkg = packageManagerInternal
+ .getPackage(sharedPkgName);
+ if (sharedPkg != null) {
+ synchroniser.addPackage(sharedPkg.getPackageName());
+ }
+ }
+ synchroniser.syncPackages();
}
/**
@@ -367,8 +381,8 @@
final PermissionToOpSynchroniser synchronizer = new PermissionToOpSynchroniser(
getUserContext(getContext(), UserHandle.of(userId)));
packageManagerInternal.forEachPackage(
- (pkg) -> synchronizer.addUid(pkg.getUid()));
- synchronizer.syncUids();
+ (pkg) -> synchronizer.addPackage(pkg.getPackageName()));
+ synchronizer.syncPackages();
}
/**
@@ -383,51 +397,37 @@
private final @NonNull ArrayMap<String, PermissionInfo> mRuntimePermissionInfos;
- // Cache uid -> packageNames
- private SparseArray<String[]> mUidToPkg = new SparseArray<>();
-
/**
* All ops that need to be flipped to allow.
*
- * @see #syncUids
+ * @see #syncPackages
*/
- private final @NonNull ArraySet<OpToChange> mOpsToAllow = new ArraySet<>();
+ private final @NonNull ArrayList<OpToChange> mOpsToAllow = new ArrayList<>();
/**
* All ops that need to be flipped to ignore.
*
- * @see #syncUids
+ * @see #syncPackages
*/
- private final @NonNull ArraySet<OpToChange> mOpsToIgnore = new ArraySet<>();
+ private final @NonNull ArrayList<OpToChange> mOpsToIgnore = new ArrayList<>();
/**
* All ops that need to be flipped to ignore if not allowed.
*
* Currently, only used by soft restricted permissions logic.
*
- * @see #syncUids
+ * @see #syncPackages
*/
- private final @NonNull ArraySet<OpToChange> mOpsToIgnoreIfNotAllowed = new ArraySet<>();
+ private final @NonNull ArrayList<OpToChange> mOpsToIgnoreIfNotAllowed = new ArrayList<>();
/**
* All ops that need to be flipped to foreground.
*
* Currently, only used by the foreground/background permissions logic.
*
- * @see #syncUids
+ * @see #syncPackages
*/
- private final @NonNull ArraySet<OpToChange> mOpsToForeground = new ArraySet<>();
-
- private @Nullable String[] getPackageNamesForUid(int uid) {
- String[] pkgs = mUidToPkg.get(uid);
- if (pkgs != null) {
- return pkgs;
- }
-
- pkgs = mPackageManager.getPackagesForUid(uid);
- mUidToPkg.put(uid, pkgs);
- return pkgs;
- }
+ private final @NonNull ArrayList<OpToChange> mOpsToForeground = new ArrayList<>();
PermissionToOpSynchroniser(@NonNull Context context) {
mContext = context;
@@ -449,11 +449,11 @@
}
/**
- * Set app ops that were added in {@link #addUid}.
+ * Set app ops that were added in {@link #addPackage}.
*
* <p>This processes ops previously added by {@link #addAppOps(PackageInfo, String)}
*/
- private void syncUids() {
+ private void syncPackages() {
// Remember which ops were already set. This makes sure that we always set the most
// permissive mode if two OpChanges are scheduled. This can e.g. happen if two
// permissions change the same op. See {@link #getSwitchOp}.
@@ -461,42 +461,42 @@
final int allowCount = mOpsToAllow.size();
for (int i = 0; i < allowCount; i++) {
- final OpToChange op = mOpsToAllow.valueAt(i);
+ final OpToChange op = mOpsToAllow.get(i);
- setUidModeAllowed(op.code, op.uid);
+ setUidModeAllowed(op.code, op.uid, op.packageName);
alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
}
final int foregroundCount = mOpsToForeground.size();
for (int i = 0; i < foregroundCount; i++) {
- final OpToChange op = mOpsToForeground.valueAt(i);
+ final OpToChange op = mOpsToForeground.get(i);
if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
continue;
}
- setUidModeForeground(op.code, op.uid);
+ setUidModeForeground(op.code, op.uid, op.packageName);
alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
}
final int ignoreCount = mOpsToIgnore.size();
for (int i = 0; i < ignoreCount; i++) {
- final OpToChange op = mOpsToIgnore.valueAt(i);
+ final OpToChange op = mOpsToIgnore.get(i);
if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
continue;
}
- setUidModeIgnored(op.code, op.uid);
+ setUidModeIgnored(op.code, op.uid, op.packageName);
alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
}
final int ignoreIfNotAllowedCount = mOpsToIgnoreIfNotAllowed.size();
for (int i = 0; i < ignoreIfNotAllowedCount; i++) {
- final OpToChange op = mOpsToIgnoreIfNotAllowed.valueAt(i);
+ final OpToChange op = mOpsToIgnoreIfNotAllowed.get(i);
if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
continue;
}
- boolean wasSet = setUidModeIgnoredIfNotAllowed(op.code, op.uid);
+ boolean wasSet = setUidModeIgnoredIfNotAllowed(op.code, op.uid, op.packageName);
if (wasSet) {
alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
}
@@ -555,7 +555,7 @@
}
int uid = packageInfo.applicationInfo.uid;
- OpToChange opToChange = new OpToChange(uid, appOpCode);
+ OpToChange opToChange = new OpToChange(uid, packageName, appOpCode);
switch (appOpMode) {
case MODE_ALLOWED:
mOpsToAllow.add(opToChange);
@@ -618,7 +618,8 @@
}
int uid = packageInfo.applicationInfo.uid;
- OpToChange extraOpToChange = new OpToChange(uid, extraOpCode);
+ String packageName = packageInfo.packageName;
+ OpToChange extraOpToChange = new OpToChange(uid, packageName, extraOpCode);
if (policy.mayAllowExtraAppOp()) {
mOpsToAllow.add(extraOpToChange);
} else {
@@ -631,56 +632,45 @@
}
/**
- * Add a Uid for {@link #syncUids() processing} later.
+ * Add a package for {@link #syncPackages() processing} later.
*
* <p>Note: Called with the package lock held. Do <u>not</u> call into app-op manager.
*
- * @param uid The uid to add for later processing.
+ * @param pkgName The package to add for later processing.
*/
- void addUid(int uid) {
- String[] pkgNames = getPackageNamesForUid(uid);
- if (pkgNames == null) {
+ void addPackage(@NonNull String pkgName) {
+ final PackageInfo pkg;
+ try {
+ pkg = mPackageManager.getPackageInfo(pkgName, GET_PERMISSIONS);
+ } catch (NameNotFoundException e) {
return;
}
- for (String pkgName : pkgNames) {
- final PackageInfo pkg;
- try {
- pkg = mPackageManager.getPackageInfo(pkgName, GET_PERMISSIONS);
- } catch (NameNotFoundException e) {
- continue;
- }
+ if (pkg.requestedPermissions == null) {
+ return;
+ }
- if (pkg.requestedPermissions == null) {
- continue;
- }
-
- for (String permission : pkg.requestedPermissions) {
- addAppOps(pkg, permission);
- }
+ for (String permission : pkg.requestedPermissions) {
+ addAppOps(pkg, permission);
}
}
- private void setUidModeAllowed(int opCode, int uid) {
- setUidMode(opCode, uid, MODE_ALLOWED);
+ private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) {
+ setUidMode(opCode, uid, MODE_ALLOWED, packageName);
}
- private void setUidModeForeground(int opCode, int uid) {
- setUidMode(opCode, uid, MODE_FOREGROUND);
+ private void setUidModeForeground(int opCode, int uid, @NonNull String packageName) {
+ setUidMode(opCode, uid, MODE_FOREGROUND, packageName);
}
- private void setUidModeIgnored(int opCode, int uid) {
- setUidMode(opCode, uid, MODE_IGNORED);
+ private void setUidModeIgnored(int opCode, int uid, @NonNull String packageName) {
+ setUidMode(opCode, uid, MODE_IGNORED, packageName);
}
- private boolean setUidModeIgnoredIfNotAllowed(int opCode, int uid) {
- String[] pkgsOfUid = getPackageNamesForUid(uid);
- if (ArrayUtils.isEmpty(pkgsOfUid)) {
- return false;
- }
-
+ private boolean setUidModeIgnoredIfNotAllowed(int opCode, int uid,
+ @NonNull String packageName) {
final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
- opCode), uid, pkgsOfUid[0]);
+ opCode), uid, packageName);
if (currentMode != MODE_ALLOWED) {
if (currentMode != MODE_IGNORED) {
mAppOpsManagerInternal.setUidModeFromPermissionPolicy(opCode, uid, MODE_IGNORED,
@@ -691,24 +681,20 @@
return false;
}
- private void setUidMode(int opCode, int uid, int mode) {
- String[] pkgsOfUid = getPackageNamesForUid(uid);
- if (ArrayUtils.isEmpty(pkgsOfUid)) {
- return;
- }
-
+ private void setUidMode(int opCode, int uid, int mode,
+ @NonNull String packageName) {
final int oldMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
- opCode), uid, pkgsOfUid[0]);
+ opCode), uid, packageName);
if (oldMode != mode) {
mAppOpsManagerInternal.setUidModeFromPermissionPolicy(opCode, uid, mode,
mAppOpsCallback);
final int newMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
- opCode), uid, pkgsOfUid[0]);
+ opCode), uid, packageName);
if (newMode != mode) {
// Work around incorrectly-set package mode. It never makes sense for app ops
// related to runtime permissions, but can get in the way and we have to reset
// it.
- mAppOpsManagerInternal.setModeFromPermissionPolicy(opCode, uid, pkgsOfUid[0],
+ mAppOpsManagerInternal.setModeFromPermissionPolicy(opCode, uid, packageName,
AppOpsManager.opToDefaultMode(opCode), mAppOpsCallback);
}
}
@@ -716,30 +702,14 @@
private class OpToChange {
final int uid;
+ final @NonNull String packageName;
final int code;
- OpToChange(int uid, int code) {
+ OpToChange(int uid, @NonNull String packageName, int code) {
this.uid = uid;
+ this.packageName = packageName;
this.code = code;
}
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- OpToChange other = (OpToChange) o;
- return uid == other.uid && code == other.code;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(uid, code);
- }
}
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index 4b3746b..42aaec9 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -176,8 +176,10 @@
@GuardedBy("mLock")
private int mSettingBatterySaverStickyAutoDisableThreshold;
- /** Config flag to track default disable threshold for Dynamic Power Savings enabled battery
- * saver. */
+ /**
+ * Config flag to track default disable threshold for Dynamic Power Savings enabled battery
+ * saver.
+ */
@GuardedBy("mLock")
private final int mDynamicPowerSavingsDefaultDisableThreshold;
@@ -192,8 +194,9 @@
@GuardedBy("mLock")
private int mSettingAutomaticBatterySaver;
- /** When to disable battery saver again if it was enabled due to an external suggestion.
- * Corresponds to Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD.
+ /**
+ * When to disable battery saver again if it was enabled due to an external suggestion.
+ * Corresponds to Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD.
*/
@GuardedBy("mLock")
private int mDynamicPowerSavingsDisableThreshold;
@@ -203,7 +206,7 @@
* Updates when Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED changes.
*/
@GuardedBy("mLock")
- private boolean mDynamicPowerSavingsBatterySaver;
+ private boolean mDynamicPowerSavingsEnableBatterySaver;
/**
* Last reason passed to {@link #enableBatterySaverLocked}.
@@ -265,7 +268,7 @@
/** @return true if the dynamic mode should be used */
private boolean isDynamicModeActiveLocked() {
return mSettingAutomaticBatterySaver == PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC
- && mDynamicPowerSavingsBatterySaver;
+ && mDynamicPowerSavingsEnableBatterySaver;
}
/**
@@ -428,7 +431,7 @@
final boolean dynamicPowerSavingsThresholdChanged =
mDynamicPowerSavingsDisableThreshold != dynamicPowerSavingsDisableThreshold;
final boolean dynamicPowerSavingsBatterySaverChanged =
- mDynamicPowerSavingsBatterySaver != dynamicPowerSavingsBatterySaver;
+ mDynamicPowerSavingsEnableBatterySaver != dynamicPowerSavingsBatterySaver;
if (!(enabledChanged || stickyChanged || thresholdChanged || automaticModeChanged
|| stickyAutoDisableEnabledChanged || stickyAutoDisableThresholdChanged
@@ -443,7 +446,7 @@
mSettingBatterySaverStickyAutoDisableThreshold = stickyAutoDisableThreshold;
mSettingAutomaticBatterySaver = automaticBatterySaver;
mDynamicPowerSavingsDisableThreshold = dynamicPowerSavingsDisableThreshold;
- mDynamicPowerSavingsBatterySaver = dynamicPowerSavingsBatterySaver;
+ mDynamicPowerSavingsEnableBatterySaver = dynamicPowerSavingsBatterySaver;
if (thresholdChanged) {
// To avoid spamming the event log, we throttle logging here.
@@ -923,6 +926,8 @@
pw.print(" mIsBatteryLevelLow=");
pw.println(mIsBatteryLevelLow);
+ pw.print(" mSettingAutomaticBatterySaver=");
+ pw.println(mSettingAutomaticBatterySaver);
pw.print(" mSettingBatterySaverEnabled=");
pw.println(mSettingBatterySaverEnabled);
pw.print(" mSettingBatterySaverEnabledSticky=");
@@ -936,6 +941,13 @@
pw.print(" mBatterySaverStickyBehaviourDisabled=");
pw.println(mBatterySaverStickyBehaviourDisabled);
+ pw.print(" mDynamicPowerSavingsDefaultDisableThreshold=");
+ pw.println(mDynamicPowerSavingsDefaultDisableThreshold);
+ pw.print(" mDynamicPowerSavingsDisableThreshold=");
+ pw.println(mDynamicPowerSavingsDisableThreshold);
+ pw.print(" mDynamicPowerSavingsEnableBatterySaver=");
+ pw.println(mDynamicPowerSavingsEnableBatterySaver);
+
pw.print(" mLastAdaptiveBatterySaverChangedExternallyElapsed=");
pw.println(mLastAdaptiveBatterySaverChangedExternallyElapsed);
}
@@ -964,6 +976,8 @@
proto.write(BatterySaverStateMachineProto.BATTERY_LEVEL, mBatteryLevel);
proto.write(BatterySaverStateMachineProto.IS_BATTERY_LEVEL_LOW, mIsBatteryLevelLow);
+ proto.write(BatterySaverStateMachineProto.SETTING_AUTOMATIC_TRIGGER,
+ mSettingAutomaticBatterySaver);
proto.write(BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_ENABLED,
mSettingBatterySaverEnabled);
proto.write(BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_ENABLED_STICKY,
@@ -979,6 +993,16 @@
mSettingBatterySaverStickyAutoDisableThreshold);
proto.write(
+ BatterySaverStateMachineProto.DEFAULT_DYNAMIC_DISABLE_THRESHOLD,
+ mDynamicPowerSavingsDefaultDisableThreshold);
+ proto.write(
+ BatterySaverStateMachineProto.DYNAMIC_DISABLE_THRESHOLD,
+ mDynamicPowerSavingsDisableThreshold);
+ proto.write(
+ BatterySaverStateMachineProto.DYNAMIC_BATTERY_SAVER_ENABLED,
+ mDynamicPowerSavingsEnableBatterySaver);
+
+ proto.write(
BatterySaverStateMachineProto
.LAST_ADAPTIVE_BATTERY_SAVER_CHANGED_EXTERNALLY_ELAPSED,
mLastAdaptiveBatterySaverChangedExternallyElapsed);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a582f21..7720f7f 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -108,6 +108,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
@@ -755,6 +756,14 @@
|| (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
modifiedFlags |= PackageManager.MATCH_INSTANT;
}
+ int privateResolveFlags = 0;
+ if (intent.isWebIntent()
+ && (intent.getFlags() & Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER) != 0) {
+ privateResolveFlags |= PackageManagerInternal.RESOLVE_NON_BROWSER_ONLY;
+ }
+ if ((intent.getFlags() & Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT) != 0) {
+ privateResolveFlags |= PackageManagerInternal.RESOLVE_NON_RESOLVER_ONLY;
+ }
// In order to allow cross-profile lookup, we clear the calling identity here.
// Note the binder identity won't affect the result, but filterCallingUid will.
@@ -764,7 +773,8 @@
final long token = Binder.clearCallingIdentity();
try {
return mService.getPackageManagerInternalLocked().resolveIntent(
- intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
+ intent, resolvedType, modifiedFlags, privateResolveFlags, userId, true,
+ filterCallingUid);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e1f713e..68504bd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -912,7 +912,7 @@
private void setShadowRenderer() {
mRenderShadowsInCompositor = Settings.Global.getInt(mContext.getContentResolver(),
- DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 0) != 0;
+ DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0;
}
PowerManager mPowerManager;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 78d6b9631..1cfd0d4 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1513,8 +1513,7 @@
// Some system windows (e.g. "Power off" dialog) don't have a task, but we would still
// associate them with some stack to enable dimming.
final DisplayContent dc = getDisplayContent();
- return mAttrs.type >= FIRST_SYSTEM_WINDOW && dc != null
- ? dc.getOrCreateRootHomeTask() : null;
+ return mAttrs.type >= FIRST_SYSTEM_WINDOW && dc != null ? dc.getRootHomeTask() : null;
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index e37ed79..2cbc3f3 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -124,8 +124,9 @@
SyncOperation op2 = SyncOperation.maybeCreateFromJobExtras(pb);
assertTrue("Account fields in extras not persisted.",
- account1.equals(op2.extras.get("acc")));
- assertTrue("Fields in extras not persisted", "String".equals(op2.extras.getString("str")));
+ account1.equals(op2.getClonedExtras().get("acc")));
+ assertTrue("Fields in extras not persisted", "String".equals(
+ op2.getClonedExtras().getString("str")));
}
@SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
new file mode 100644
index 0000000..c4fea77
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.om;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.om.OverlayInfo;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceImplTestsBase {
+
+ private static final String OVERLAY = "com.dummy.overlay";
+ private static final String TARGET = "com.dummy.target";
+ private static final int USER = 0;
+
+ private static final String OVERLAY2 = OVERLAY + "2";
+
+ @Test
+ public void testUpdateOverlaysForUser() {
+ final OverlayManagerServiceImpl impl = getImpl();
+ installTargetPackage(TARGET, USER);
+ installTargetPackage("some.other.target", USER);
+ installOverlayPackage(OVERLAY, TARGET, USER);
+
+ // do nothing, expect no change
+ final List<String> a = impl.updateOverlaysForUser(USER);
+ assertEquals(1, a.size());
+ assertTrue(a.contains(TARGET));
+
+ // upgrade overlay, keep target
+ beginUpgradeOverlayPackage(OVERLAY, USER);
+ endUpgradeOverlayPackage(OVERLAY, TARGET, USER);
+
+ final List<String> b = impl.updateOverlaysForUser(USER);
+ assertEquals(1, b.size());
+ assertTrue(b.contains(TARGET));
+
+ // do nothing, expect no change
+ final List<String> c = impl.updateOverlaysForUser(USER);
+ assertEquals(1, c.size());
+ assertTrue(c.contains(TARGET));
+
+ // upgrade overlay, switch to new target
+ addOverlayPackage(OVERLAY, "some.other.target", USER, true, false, 0);
+ final List<String> d = impl.updateOverlaysForUser(USER);
+ assertEquals(2, d.size());
+ assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target")));
+
+ // do nothing, expect no change
+ final List<String> f = impl.updateOverlaysForUser(USER);
+ assertEquals(1, f.size());
+ assertTrue(f.contains("some.other.target"));
+ }
+
+ @Test
+ public void testImmutableEnabledChange() {
+ final OverlayManagerServiceImpl impl = getImpl();
+ installTargetPackage(TARGET, USER);
+
+ addOverlayPackage(OVERLAY, TARGET, USER, false, false, 0);
+ impl.updateOverlaysForUser(USER);
+ final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
+ assertNotNull(o1);
+ assertFalse(o1.isEnabled());
+ assertFalse(o1.isMutable);
+
+ addOverlayPackage(OVERLAY, TARGET, USER, false, true, 0);
+ impl.updateOverlaysForUser(USER);
+ final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER);
+ assertNotNull(o2);
+ assertTrue(o2.isEnabled());
+ assertFalse(o2.isMutable);
+
+ addOverlayPackage(OVERLAY, TARGET, USER, false, false, 0);
+ impl.updateOverlaysForUser(USER);
+ final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
+ assertNotNull(o3);
+ assertFalse(o3.isEnabled());
+ assertFalse(o3.isMutable);
+ }
+
+ @Test
+ public void testMutableEnabledChangeHasNoEffect() {
+ final OverlayManagerServiceImpl impl = getImpl();
+ installTargetPackage(TARGET, USER);
+
+ addOverlayPackage(OVERLAY, TARGET, USER, true, false, 0);
+ impl.updateOverlaysForUser(USER);
+ final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
+ assertNotNull(o1);
+ assertFalse(o1.isEnabled());
+ assertTrue(o1.isMutable);
+
+ addOverlayPackage(OVERLAY, TARGET, USER, true, true, 0);
+ impl.updateOverlaysForUser(USER);
+ final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER);
+ assertNotNull(o2);
+ assertFalse(o2.isEnabled());
+ assertTrue(o2.isMutable);
+
+ addOverlayPackage(OVERLAY, TARGET, USER, true, false, 0);
+ impl.updateOverlaysForUser(USER);
+ final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
+ assertNotNull(o3);
+ assertFalse(o3.isEnabled());
+ assertTrue(o3.isMutable);
+ }
+
+ @Test
+ public void testMutabilityChange() {
+ final OverlayManagerServiceImpl impl = getImpl();
+ installTargetPackage(TARGET, USER);
+
+ addOverlayPackage(OVERLAY, TARGET, USER, false, true, 0);
+ impl.updateOverlaysForUser(USER);
+ final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
+ assertNotNull(o1);
+ assertTrue(o1.isEnabled());
+ assertFalse(o1.isMutable);
+
+ addOverlayPackage(OVERLAY, TARGET, USER, true, false, 0);
+ impl.updateOverlaysForUser(USER);
+ final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER);
+ assertNotNull(o2);
+ assertFalse(o2.isEnabled());
+ assertTrue(o2.isMutable);
+
+ addOverlayPackage(OVERLAY, TARGET, USER, false, false, 0);
+ impl.updateOverlaysForUser(USER);
+ final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
+ assertNotNull(o3);
+ assertFalse(o3.isEnabled());
+ assertFalse(o3.isMutable);
+ }
+
+ @Test
+ public void testPriorityChange() {
+ final OverlayManagerServiceImpl impl = getImpl();
+ installTargetPackage(TARGET, USER);
+
+ addOverlayPackage(OVERLAY, TARGET, USER, false, true, 0);
+ addOverlayPackage(OVERLAY2, TARGET, USER, false, true, 1);
+ impl.updateOverlaysForUser(USER);
+
+ final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
+ assertNotNull(o1);
+ assertNotNull(o2);
+ assertEquals(0, o1.priority);
+ assertEquals(1, o2.priority);
+
+ addOverlayPackage(OVERLAY, TARGET, USER, false, true, 1);
+ addOverlayPackage(OVERLAY2, TARGET, USER, false, true, 0);
+ impl.updateOverlaysForUser(USER);
+
+ final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER);
+ assertNotNull(o3);
+ assertNotNull(o4);
+ assertEquals(1, o3.priority);
+ assertEquals(0, o4.priority);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index c566dfc..a428a97 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -24,6 +24,9 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.content.om.OverlayInfo;
@@ -33,6 +36,8 @@
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.content.om.OverlayConfig;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,10 +50,7 @@
import java.util.Set;
@RunWith(AndroidJUnit4.class)
-public class OverlayManagerServiceImplTests {
- private OverlayManagerServiceImpl mImpl;
- private DummyDeviceState mState;
- private DummyListener mListener;
+public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
private static final String OVERLAY = "com.dummy.overlay";
private static final String TARGET = "com.dummy.target";
@@ -61,25 +63,14 @@
private static final String OVERLAY3 = OVERLAY + "3";
private static final int USER3 = USER2 + 1;
-
- @Before
- public void setUp() throws Exception {
- mState = new DummyDeviceState();
- mListener = new DummyListener();
- DummyPackageManagerHelper pmh = new DummyPackageManagerHelper(mState);
- mImpl = new OverlayManagerServiceImpl(pmh,
- new DummyIdmapManager(mState, pmh),
- new OverlayManagerSettings(),
- new String[0],
- mListener);
- }
-
// tests: basics
@Test
public void testGetOverlayInfo() throws Exception {
- installOverlayPackage(OVERLAY, TARGET, USER, false);
- final OverlayInfo oi = mImpl.getOverlayInfo(OVERLAY, USER);
+ installOverlayPackage(OVERLAY, TARGET, USER);
+
+ final OverlayManagerServiceImpl impl = getImpl();
+ final OverlayInfo oi = impl.getOverlayInfo(OVERLAY, USER);
assertNotNull(oi);
assertEquals(oi.packageName, OVERLAY);
assertEquals(oi.targetPackageName, TARGET);
@@ -88,87 +79,91 @@
@Test
public void testGetOverlayInfosForTarget() throws Exception {
- installOverlayPackage(OVERLAY, TARGET, USER, false);
- installOverlayPackage(OVERLAY2, TARGET, USER, false);
+ installOverlayPackage(OVERLAY, TARGET, USER);
+ installOverlayPackage(OVERLAY2, TARGET, USER);
+ installOverlayPackage(OVERLAY3, TARGET, USER2);
- installOverlayPackage(OVERLAY3, TARGET, USER2, false);
-
- final List<OverlayInfo> ois = mImpl.getOverlayInfosForTarget(TARGET, USER);
+ final OverlayManagerServiceImpl impl = getImpl();
+ final List<OverlayInfo> ois = impl.getOverlayInfosForTarget(TARGET, USER);
assertEquals(ois.size(), 2);
- assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY, USER)));
- assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY2, USER)));
+ assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY, USER)));
+ assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY2, USER)));
- final List<OverlayInfo> ois2 = mImpl.getOverlayInfosForTarget(TARGET, USER2);
+ final List<OverlayInfo> ois2 = impl.getOverlayInfosForTarget(TARGET, USER2);
assertEquals(ois2.size(), 1);
- assertTrue(ois2.contains(mImpl.getOverlayInfo(OVERLAY3, USER2)));
+ assertTrue(ois2.contains(impl.getOverlayInfo(OVERLAY3, USER2)));
- final List<OverlayInfo> ois3 = mImpl.getOverlayInfosForTarget(TARGET, USER3);
+ final List<OverlayInfo> ois3 = impl.getOverlayInfosForTarget(TARGET, USER3);
assertNotNull(ois3);
assertEquals(ois3.size(), 0);
- final List<OverlayInfo> ois4 = mImpl.getOverlayInfosForTarget("no.such.overlay", USER);
+ final List<OverlayInfo> ois4 = impl.getOverlayInfosForTarget("no.such.overlay", USER);
assertNotNull(ois4);
assertEquals(ois4.size(), 0);
}
@Test
public void testGetOverlayInfosForUser() throws Exception {
- installOverlayPackage(OVERLAY, TARGET, USER, false);
- installOverlayPackage(OVERLAY2, TARGET, USER, false);
- installOverlayPackage(OVERLAY3, TARGET2, USER, false);
+ installTargetPackage(TARGET, USER);
+ installOverlayPackage(OVERLAY, TARGET, USER);
+ installOverlayPackage(OVERLAY2, TARGET, USER);
+ installOverlayPackage(OVERLAY3, TARGET2, USER);
- final Map<String, List<OverlayInfo>> everything = mImpl.getOverlaysForUser(USER);
+ final OverlayManagerServiceImpl impl = getImpl();
+ final Map<String, List<OverlayInfo>> everything = impl.getOverlaysForUser(USER);
assertEquals(everything.size(), 2);
final List<OverlayInfo> ois = everything.get(TARGET);
assertNotNull(ois);
assertEquals(ois.size(), 2);
- assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY, USER)));
- assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY2, USER)));
+ assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY, USER)));
+ assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY2, USER)));
final List<OverlayInfo> ois2 = everything.get(TARGET2);
assertNotNull(ois2);
assertEquals(ois2.size(), 1);
- assertTrue(ois2.contains(mImpl.getOverlayInfo(OVERLAY3, USER)));
+ assertTrue(ois2.contains(impl.getOverlayInfo(OVERLAY3, USER)));
- final Map<String, List<OverlayInfo>> everything2 = mImpl.getOverlaysForUser(USER2);
+ final Map<String, List<OverlayInfo>> everything2 = impl.getOverlaysForUser(USER2);
assertNotNull(everything2);
assertEquals(everything2.size(), 0);
}
@Test
public void testPriority() throws Exception {
- installOverlayPackage(OVERLAY, TARGET, USER, false);
- installOverlayPackage(OVERLAY2, TARGET, USER, false);
- installOverlayPackage(OVERLAY3, TARGET, USER, false);
+ installOverlayPackage(OVERLAY, TARGET, USER);
+ installOverlayPackage(OVERLAY2, TARGET, USER);
+ installOverlayPackage(OVERLAY3, TARGET, USER);
- final OverlayInfo o1 = mImpl.getOverlayInfo(OVERLAY, USER);
- final OverlayInfo o2 = mImpl.getOverlayInfo(OVERLAY2, USER);
- final OverlayInfo o3 = mImpl.getOverlayInfo(OVERLAY3, USER);
+ final OverlayManagerServiceImpl impl = getImpl();
+ final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
+ final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY3, USER);
assertOverlayInfoList(TARGET, USER, o1, o2, o3);
- assertTrue(mImpl.setLowestPriority(OVERLAY3, USER));
+ assertTrue(impl.setLowestPriority(OVERLAY3, USER));
assertOverlayInfoList(TARGET, USER, o3, o1, o2);
- assertTrue(mImpl.setHighestPriority(OVERLAY3, USER));
+ assertTrue(impl.setHighestPriority(OVERLAY3, USER));
assertOverlayInfoList(TARGET, USER, o1, o2, o3);
- assertTrue(mImpl.setPriority(OVERLAY, OVERLAY2, USER));
+ assertTrue(impl.setPriority(OVERLAY, OVERLAY2, USER));
assertOverlayInfoList(TARGET, USER, o2, o1, o3);
}
@Test
public void testOverlayInfoStateTransitions() throws Exception {
- assertNull(mImpl.getOverlayInfo(OVERLAY, USER));
+ final OverlayManagerServiceImpl impl = getImpl();
+ assertNull(impl.getOverlayInfo(OVERLAY, USER));
- installOverlayPackage(OVERLAY, TARGET, USER, true);
+ installOverlayPackage(OVERLAY, TARGET, USER);
assertState(STATE_MISSING_TARGET, OVERLAY, USER);
installTargetPackage(TARGET, USER);
assertState(STATE_DISABLED, OVERLAY, USER);
- mImpl.setEnabled(OVERLAY, true, USER);
+ impl.setEnabled(OVERLAY, true, USER);
assertState(STATE_ENABLED, OVERLAY, USER);
// target upgrades do not change the state of the overlay
@@ -186,319 +181,48 @@
}
@Test
- public void testUpdateOverlaysForUser() throws Exception {
- installTargetPackage(TARGET, USER);
- installTargetPackage("some.other.target", USER);
- installOverlayPackage(OVERLAY, TARGET, USER, true);
-
- // do nothing, expect no change
- List<String> a = mImpl.updateOverlaysForUser(USER);
- assertEquals(1, a.size());
- assertTrue(a.contains(TARGET));
-
- // upgrade overlay, keep target
- upgradeOverlayPackage(OVERLAY, TARGET, USER, true);
- List<String> b = mImpl.updateOverlaysForUser(USER);
- assertEquals(1, b.size());
- assertTrue(b.contains(TARGET));
-
- // do nothing, expect no change
- List<String> c = mImpl.updateOverlaysForUser(USER);
- assertEquals(1, c.size());
- assertTrue(c.contains(TARGET));
-
- // upgrade overlay, switch to new target
- upgradeOverlayPackage(OVERLAY, "some.other.target", USER, true);
- List<String> d = mImpl.updateOverlaysForUser(USER);
- assertEquals(2, d.size());
- assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target")));
-
- // do nothing, expect no change
- List<String> e = mImpl.updateOverlaysForUser(USER);
- assertEquals(1, e.size());
- assertTrue(e.contains("some.other.target"));
- }
-
- @Test
public void testOnOverlayPackageUpgraded() throws Exception {
+ final OverlayManagerServiceImpl impl = getImpl();
+ final DummyListener listener = getListener();
installTargetPackage(TARGET, USER);
- installOverlayPackage(OVERLAY, TARGET, USER, true);
- mImpl.onOverlayPackageReplacing(OVERLAY, USER);
- mListener.count = 0;
- mImpl.onOverlayPackageReplaced(OVERLAY, USER);
- assertEquals(1, mListener.count);
+ installOverlayPackage(OVERLAY, TARGET, USER);
+ impl.onOverlayPackageReplacing(OVERLAY, USER);
+ listener.count = 0;
+ impl.onOverlayPackageReplaced(OVERLAY, USER);
+ assertEquals(1, listener.count);
// upgrade to a version where the overlay has changed its target
- upgradeOverlayPackage(OVERLAY, "some.other.target", USER, true);
- mImpl.onOverlayPackageReplacing(OVERLAY, USER);
- mListener.count = 0;
- mImpl.onOverlayPackageReplaced(OVERLAY, USER);
+ beginUpgradeOverlayPackage(OVERLAY, USER);
+ listener.count = 0;
+ endUpgradeOverlayPackage(OVERLAY, "some.other.target", USER);
// expect once for the old target package, once for the new target package
- assertEquals(2, mListener.count);
+ assertEquals(2, listener.count);
- upgradeOverlayPackage(OVERLAY, "some.other.target", USER, true);
- mImpl.onOverlayPackageReplacing(OVERLAY, USER);
- mListener.count = 0;
- mImpl.onOverlayPackageReplaced(OVERLAY, USER);
- assertEquals(1, mListener.count);
+ beginUpgradeOverlayPackage(OVERLAY, USER);
+ listener.count = 0;
+ endUpgradeOverlayPackage(OVERLAY, "some.other.target", USER);
+ assertEquals(1, listener.count);
}
// tests: listener interface
@Test
public void testListener() throws Exception {
- installOverlayPackage(OVERLAY, TARGET, USER, true);
- assertEquals(1, mListener.count);
- mListener.count = 0;
+ final OverlayManagerServiceImpl impl = getImpl();
+ final DummyListener listener = getListener();
+ installOverlayPackage(OVERLAY, TARGET, USER);
+ assertEquals(1, listener.count);
+ listener.count = 0;
installTargetPackage(TARGET, USER);
- assertEquals(1, mListener.count);
- mListener.count = 0;
+ assertEquals(1, listener.count);
+ listener.count = 0;
- mImpl.setEnabled(OVERLAY, true, USER);
- assertEquals(1, mListener.count);
- mListener.count = 0;
+ impl.setEnabled(OVERLAY, true, USER);
+ assertEquals(1, listener.count);
+ listener.count = 0;
- mImpl.setEnabled(OVERLAY, true, USER);
- assertEquals(0, mListener.count);
- }
-
- // helper methods
-
- private void assertState(int expected, final String overlayPackageName, int userId) {
- int actual = mImpl.getOverlayInfo(OVERLAY, USER).state;
- String msg = String.format("expected %s but was %s:",
- OverlayInfo.stateToString(expected), OverlayInfo.stateToString(actual));
- assertEquals(msg, expected, actual);
- }
-
- private void assertOverlayInfoList(final String targetPackageName, int userId,
- OverlayInfo... overlayInfos) {
- final List<OverlayInfo> expected =
- mImpl.getOverlayInfosForTarget(targetPackageName, userId);
- final List<OverlayInfo> actual = Arrays.asList(overlayInfos);
- assertEquals(expected, actual);
- }
-
- private void installTargetPackage(String packageName, int userId) {
- if (mState.select(packageName, userId) != null) {
- throw new IllegalStateException("package already installed");
- }
- mState.add(packageName, null, userId, false);
- mImpl.onTargetPackageAdded(packageName, userId);
- }
-
- private void beginUpgradeTargetPackage(String packageName, int userId) {
- if (mState.select(packageName, userId) == null) {
- throw new IllegalStateException("package not installed");
- }
- mState.add(packageName, null, userId, false);
- mImpl.onTargetPackageReplacing(packageName, userId);
- }
-
- private void endUpgradeTargetPackage(String packageName, int userId) {
- if (mState.select(packageName, userId) == null) {
- throw new IllegalStateException("package not installed");
- }
- mState.add(packageName, null, userId, false);
- mImpl.onTargetPackageReplaced(packageName, userId);
- }
-
- private void uninstallTargetPackage(String packageName, int userId) {
- if (mState.select(packageName, userId) == null) {
- throw new IllegalStateException("package not installed");
- }
- mState.remove(packageName, userId);
- mImpl.onTargetPackageRemoved(packageName, userId);
- }
-
- private void installOverlayPackage(String packageName, String targetPackageName, int userId,
- boolean canCreateIdmap) {
- if (mState.select(packageName, userId) != null) {
- throw new IllegalStateException("package already installed");
- }
- mState.add(packageName, targetPackageName, userId, canCreateIdmap);
- mImpl.onOverlayPackageAdded(packageName, userId);
- }
-
- private void upgradeOverlayPackage(String packageName, String targetPackageName, int userId,
- boolean canCreateIdmap) {
- DummyDeviceState.Package pkg = mState.select(packageName, userId);
- if (pkg == null) {
- throw new IllegalStateException("package not installed, cannot upgrade");
- }
- pkg.targetPackageName = targetPackageName;
- pkg.canCreateIdmap = canCreateIdmap;
- }
-
- private void uninstallOverlayPackage(String packageName, int userId) {
- // implement this when adding support for downloadable overlays
- throw new IllegalArgumentException("not implemented");
- }
-
- private static final class DummyDeviceState {
- private List<Package> mPackages = new ArrayList<>();
-
- public void add(String packageName, String targetPackageName, int userId,
- boolean canCreateIdmap) {
- remove(packageName, userId);
- Package pkg = new Package();
- pkg.packageName = packageName;
- pkg.targetPackageName = targetPackageName;
- pkg.userId = userId;
- pkg.canCreateIdmap = canCreateIdmap;
- mPackages.add(pkg);
- }
-
- public void remove(String packageName, int userId) {
- final Iterator<Package> iter = mPackages.iterator();
- while (iter.hasNext()) {
- final Package pkg = iter.next();
- if (pkg.packageName.equals(packageName) && pkg.userId == userId) {
- iter.remove();
- return;
- }
- }
- }
-
- public List<Package> select(int userId) {
- List<Package> out = new ArrayList<>();
- final int packageCount = mPackages.size();
- for (int i = 0; i < packageCount; i++) {
- final Package pkg = mPackages.get(i);
- if (pkg.userId == userId) {
- out.add(pkg);
- }
- }
- return out;
- }
-
- public Package select(String packageName, int userId) {
- final int packageCount = mPackages.size();
- for (int i = 0; i < packageCount; i++) {
- final Package pkg = mPackages.get(i);
- if (pkg.packageName.equals(packageName) && pkg.userId == userId) {
- return pkg;
- }
- }
- return null;
- }
-
- private static final class Package {
- public String packageName;
- public int userId;
- public String targetPackageName;
- public boolean canCreateIdmap;
- }
- }
-
- private static final class DummyPackageManagerHelper implements
- OverlayManagerServiceImpl.PackageManagerHelper {
- private final DummyDeviceState mState;
-
- DummyPackageManagerHelper(DummyDeviceState state) {
- mState = state;
- }
-
- @Override
- public PackageInfo getPackageInfo(@NonNull String packageName, int userId) {
- final DummyDeviceState.Package pkg = mState.select(packageName, userId);
- if (pkg == null) {
- return null;
- }
- ApplicationInfo ai = new ApplicationInfo();
- ai.sourceDir = String.format("%s/%s/base.apk",
- pkg.targetPackageName == null ? "/system/app/" : "/vendor/overlay/",
- pkg.packageName);
- PackageInfo pi = new PackageInfo();
- pi.applicationInfo = ai;
- pi.packageName = pkg.packageName;
- pi.overlayTarget = pkg.targetPackageName;
- pi.overlayCategory = "dummy-category-" + pkg.targetPackageName;
- return pi;
- }
-
- @Override
- public boolean signaturesMatching(@NonNull String packageName1,
- @NonNull String packageName2, int userId) {
- return false;
- }
-
- @Override
- public List<PackageInfo> getOverlayPackages(int userId) {
- List<PackageInfo> out = new ArrayList<>();
- final List<DummyDeviceState.Package> packages = mState.select(userId);
- final int packageCount = packages.size();
- for (int i = 0; i < packageCount; i++) {
- final DummyDeviceState.Package pkg = packages.get(i);
- if (pkg.targetPackageName != null) {
- out.add(getPackageInfo(pkg.packageName, pkg.userId));
- }
- }
- return out;
- }
- }
-
- private static class DummyIdmapManager extends IdmapManager {
- private final DummyDeviceState mState;
- private Set<String> mIdmapFiles = new ArraySet<>();
-
- DummyIdmapManager(DummyDeviceState state, DummyPackageManagerHelper packageManagerHelper) {
- super(packageManagerHelper);
- mState = state;
- }
-
- @Override
- boolean createIdmap(@NonNull final PackageInfo targetPackage,
- @NonNull final PackageInfo overlayPackage, int userId) {
- final DummyDeviceState.Package t = mState.select(targetPackage.packageName, userId);
- if (t == null) {
- return false;
- }
- final DummyDeviceState.Package o = mState.select(overlayPackage.packageName, userId);
- if (o == null) {
- return false;
- }
- if (!o.canCreateIdmap) {
- return false;
- }
- final String key = createKey(overlayPackage.packageName, userId);
- mIdmapFiles.add(key);
- return true;
- }
-
- @Override
- boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
- final String key = createKey(oi.packageName, oi.userId);
- if (!mIdmapFiles.contains(key)) {
- return false;
- }
- mIdmapFiles.remove(key);
- return true;
- }
-
- @Override
- boolean idmapExists(@NonNull final OverlayInfo oi) {
- final String key = createKey(oi.packageName, oi.userId);
- return mIdmapFiles.contains(key);
- }
-
- @Override
- boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) {
- final String key = createKey(overlayPackage.packageName, userId);
- return mIdmapFiles.contains(key);
- }
-
- private String createKey(@NonNull final String packageName, final int userId) {
- return String.format("%s:%d", packageName, userId);
- }
- }
-
- private static class DummyListener implements OverlayManagerServiceImpl.OverlayChangeListener {
- public int count;
-
- public void onOverlaysChanged(@NonNull String targetPackage, int userId) {
- count++;
- }
+ impl.setEnabled(OVERLAY, true, USER);
+ assertEquals(0, listener.count);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
new file mode 100644
index 0000000..a753aac
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.om;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.om.OverlayInfo;
+import android.content.om.OverlayInfo.State;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.util.ArraySet;
+
+import com.android.internal.content.om.OverlayConfig;
+
+import org.junit.Before;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
+class OverlayManagerServiceImplTestsBase {
+ private OverlayManagerServiceImpl mImpl;
+ private DummyDeviceState mState;
+ private DummyListener mListener;
+
+ @Before
+ public void setUp() {
+ mState = new DummyDeviceState();
+ mListener = new DummyListener();
+ final DummyPackageManagerHelper pmh = new DummyPackageManagerHelper(mState);
+
+ mImpl = new OverlayManagerServiceImpl(pmh,
+ new DummyIdmapManager(mState, pmh),
+ new OverlayManagerSettings(),
+ mState.mOverlayConfig,
+ new String[0],
+ mListener);
+ }
+
+ public OverlayManagerServiceImpl getImpl() {
+ return mImpl;
+ }
+
+ public DummyListener getListener() {
+ return mListener;
+ }
+
+ void assertState(@State int expected, final String overlayPackageName, int userId) {
+ final OverlayInfo info = mImpl.getOverlayInfo(overlayPackageName, userId);
+ if (info == null) {
+ throw new IllegalStateException("package not installed");
+ }
+
+ final String msg = String.format("expected %s but was %s:",
+ OverlayInfo.stateToString(expected), OverlayInfo.stateToString(info.state));
+ assertEquals(msg, expected, info.state);
+ }
+
+ void assertOverlayInfoList(final String targetPackageName, int userId,
+ OverlayInfo... overlayInfos) {
+ final List<OverlayInfo> expected =
+ mImpl.getOverlayInfosForTarget(targetPackageName, userId);
+ final List<OverlayInfo> actual = Arrays.asList(overlayInfos);
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Creates an overlay configured through {@link OverlayConfig}.
+ *
+ * @throws IllegalStateException if the package is already installed
+ */
+ void addOverlayPackage(String packageName, String targetPackageName, int userId,
+ boolean mutable, boolean enabled, int priority) {
+ mState.addOverlay(packageName, targetPackageName, userId, mutable, enabled, priority);
+ }
+
+ /**
+ * Adds the target package to the device.
+ *
+ * This corresponds to when the OMS receives the
+ * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast.
+ *
+ * @throws IllegalStateException if the package is not currently installed
+ */
+ void installTargetPackage(String packageName, int userId) {
+ if (mState.select(packageName, userId) != null) {
+ throw new IllegalStateException("package already installed");
+ }
+ mState.addTarget(packageName, userId);
+ mImpl.onTargetPackageAdded(packageName, userId);
+ }
+
+ /**
+ * Begins upgrading the target package.
+ *
+ * This corresponds to when the OMS receives the
+ * {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast with the
+ * {@link android.content.Intent#EXTRA_REPLACING} extra.
+ *
+ * @throws IllegalStateException if the package is not currently installed
+ */
+ void beginUpgradeTargetPackage(String packageName, int userId) {
+ if (mState.select(packageName, userId) == null) {
+ throw new IllegalStateException("package not installed");
+ }
+ mImpl.onTargetPackageReplacing(packageName, userId);
+ }
+
+ /**
+ * Ends upgrading the target package.
+ *
+ * This corresponds to when the OMS receives the
+ * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the
+ * {@link android.content.Intent#EXTRA_REPLACING} extra.
+ *
+ * @throws IllegalStateException if the package is not currently installed
+ */
+ void endUpgradeTargetPackage(String packageName, int userId) {
+ if (mState.select(packageName, userId) == null) {
+ throw new IllegalStateException("package not installed");
+ }
+ mState.addTarget(packageName, userId);
+ mImpl.onTargetPackageReplaced(packageName, userId);
+ }
+
+ /**
+ * Removes the target package from the device.
+ *
+ * This corresponds to when the OMS receives the
+ * {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast.
+ *
+ * @throws IllegalStateException if the package is not currently installed
+ */
+ void uninstallTargetPackage(String packageName, int userId) {
+ if (mState.select(packageName, userId) == null) {
+ throw new IllegalStateException("package not installed");
+ }
+ mState.remove(packageName, userId);
+ mImpl.onTargetPackageRemoved(packageName, userId);
+ }
+
+ /**
+ * Adds the overlay package to the device.
+ *
+ * This corresponds to when the OMS receives the
+ * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast.
+ *
+ * @throws IllegalStateException if the package is already installed
+ */
+ void installOverlayPackage(String packageName, String targetPackageName, int userId) {
+ if (mState.select(packageName, userId) != null) {
+ throw new IllegalStateException("package already installed");
+ }
+ mState.addOverlay(packageName, targetPackageName, userId);
+ mImpl.onOverlayPackageAdded(packageName, userId);
+ }
+
+ /**
+ * Begins upgrading the overlay package.
+ *
+ * This corresponds to when the OMS receives the
+ * {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast with the
+ * {@link android.content.Intent#EXTRA_REPLACING} extra.
+ *
+ * @throws IllegalStateException if the package is not currently installed
+ */
+ void beginUpgradeOverlayPackage(String packageName, int userId) {
+ if (mState.select(packageName, userId) == null) {
+ throw new IllegalStateException("package not installed, cannot upgrade");
+ }
+
+ mImpl.onOverlayPackageReplacing(packageName, userId);
+ }
+
+ /**
+ * Ends upgrading the overlay package, potentially changing its target package.
+ *
+ * This corresponds to when the OMS receives the
+ * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the
+ * {@link android.content.Intent#EXTRA_REPLACING} extra.
+ *
+ * @throws IllegalStateException if the package is not currently installed
+ */
+ void endUpgradeOverlayPackage(String packageName, String targetPackageName, int userId) {
+ if (mState.select(packageName, userId) == null) {
+ throw new IllegalStateException("package not installed, cannot upgrade");
+ }
+
+ mState.addOverlay(packageName, targetPackageName, userId);
+ mImpl.onOverlayPackageReplaced(packageName, userId);
+ }
+
+ private static final class DummyDeviceState {
+ private List<Package> mPackages = new ArrayList<>();
+ private OverlayConfig mOverlayConfig = mock(OverlayConfig.class);
+
+ /** Adds a non-overlay to the device. */
+ public void addTarget(String packageName, int userId) {
+ remove(packageName, userId);
+ mPackages.add(new Package(packageName, userId, null, false, false, 0));
+ }
+
+ /** Adds an overlay to the device. */
+ public void addOverlay(String packageName, String targetPackageName, int userId) {
+ addOverlay(packageName, targetPackageName, userId, true, false, OverlayConfig.DEFAULT_PRIORITY);
+ }
+
+ /** Adds a configured overlay to the device. */
+ public void addOverlay(String packageName, String targetPackageName, int userId,
+ boolean mutable, boolean enabled, int priority) {
+ remove(packageName, userId);
+ mPackages.add(new Package(packageName, userId, targetPackageName, mutable, enabled,
+ priority));
+ when(mOverlayConfig.getPriority(packageName)).thenReturn(priority);
+ when(mOverlayConfig.isEnabled(packageName)).thenReturn(enabled);
+ when(mOverlayConfig.isMutable(packageName)).thenReturn(mutable);
+ }
+
+ /** Remove a package from the device. */
+ public void remove(String packageName, int userId) {
+ final Iterator<Package> iter = mPackages.iterator();
+ while (iter.hasNext()) {
+ final Package pkg = iter.next();
+ if (pkg.packageName.equals(packageName) && pkg.userId == userId) {
+ iter.remove();
+ return;
+ }
+ }
+ }
+
+ /** Retrieves all packages on device for a particular user. */
+ public List<Package> select(int userId) {
+ return mPackages.stream().filter(p -> p.userId == userId).collect(Collectors.toList());
+ }
+
+ /** Retrieves the package with the specified package name for a particular user. */
+ public Package select(String packageName, int userId) {
+ return mPackages.stream().filter(
+ p -> p.packageName.equals(packageName) && p.userId == userId)
+ .findFirst().orElse(null);
+ }
+
+ private static final class Package {
+ public final String packageName;
+ public final int userId;
+ public final String targetPackageName;
+ public final boolean mutable;
+ public final boolean enabled;
+ public final int priority;
+
+ private Package(String packageName, int userId, String targetPackageName,
+ boolean mutable, boolean enabled, int priority) {
+ this.packageName = packageName;
+ this.userId = userId;
+ this.targetPackageName = targetPackageName;
+ this.mutable = mutable;
+ this.enabled = enabled;
+ this.priority = priority;
+ }
+ }
+ }
+
+ static final class DummyPackageManagerHelper implements
+ OverlayManagerServiceImpl.PackageManagerHelper {
+ private final DummyDeviceState mState;
+
+ private DummyPackageManagerHelper(DummyDeviceState state) {
+ mState = state;
+ }
+
+ @Override
+ public PackageInfo getPackageInfo(@NonNull String packageName, int userId) {
+ final DummyDeviceState.Package pkg = mState.select(packageName, userId);
+ if (pkg == null) {
+ return null;
+ }
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.sourceDir = String.format("%s/%s/base.apk",
+ pkg.targetPackageName == null ? "/system/app/" : "/vendor/overlay/",
+ pkg.packageName);
+ PackageInfo pi = new PackageInfo();
+ pi.applicationInfo = ai;
+ pi.packageName = pkg.packageName;
+ pi.overlayTarget = pkg.targetPackageName;
+ pi.overlayCategory = "dummy-category-" + pkg.targetPackageName;
+ return pi;
+ }
+
+ @Override
+ public boolean signaturesMatching(@NonNull String packageName1,
+ @NonNull String packageName2, int userId) {
+ return false;
+ }
+
+ @Override
+ public List<PackageInfo> getOverlayPackages(int userId) {
+ return mState.select(userId).stream()
+ .filter(p -> p.targetPackageName != null)
+ .map(p -> getPackageInfo(p.packageName, p.userId))
+ .collect(Collectors.toList());
+ }
+ }
+
+ static class DummyIdmapManager extends IdmapManager {
+ private final DummyDeviceState mState;
+ private Set<String> mIdmapFiles = new ArraySet<>();
+
+ private DummyIdmapManager(DummyDeviceState state,
+ DummyPackageManagerHelper packageManagerHelper) {
+ super(packageManagerHelper);
+ mState = state;
+ }
+
+ @Override
+ boolean createIdmap(@NonNull final PackageInfo targetPackage,
+ @NonNull final PackageInfo overlayPackage, int userId) {
+ final DummyDeviceState.Package t = mState.select(targetPackage.packageName, userId);
+ if (t == null) {
+ return false;
+ }
+ final DummyDeviceState.Package o = mState.select(overlayPackage.packageName, userId);
+ if (o == null) {
+ return false;
+ }
+ final String key = createKey(overlayPackage.packageName, userId);
+ mIdmapFiles.add(key);
+ return true;
+ }
+
+ @Override
+ boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
+ final String key = createKey(oi.packageName, oi.userId);
+ if (!mIdmapFiles.contains(key)) {
+ return false;
+ }
+ mIdmapFiles.remove(key);
+ return true;
+ }
+
+ @Override
+ boolean idmapExists(@NonNull final OverlayInfo oi) {
+ final String key = createKey(oi.packageName, oi.userId);
+ return mIdmapFiles.contains(key);
+ }
+
+ @Override
+ boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) {
+ final String key = createKey(overlayPackage.packageName, userId);
+ return mIdmapFiles.contains(key);
+ }
+
+ private String createKey(@NonNull final String packageName, final int userId) {
+ return String.format("%s:%d", packageName, userId);
+ }
+ }
+
+ static class DummyListener implements OverlayManagerServiceImpl.OverlayChangeListener {
+ public int count;
+
+ public void onOverlaysChanged(@NonNull String targetPackage, int userId) {
+ count++;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
index 8ff8b6e..146f60a 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
@@ -58,7 +58,7 @@
STATE_DISABLED,
0,
0,
- false);
+ true);
private static final OverlayInfo OVERLAY_B0 = new OverlayInfo(
"com.dummy.overlay_b",
@@ -69,7 +69,7 @@
STATE_DISABLED,
0,
0,
- false);
+ true);
private static final OverlayInfo OVERLAY_C0 = new OverlayInfo(
"com.dummy.overlay_c",
@@ -80,7 +80,7 @@
STATE_DISABLED,
0,
0,
- false);
+ true);
private static final OverlayInfo OVERLAY_A1 = new OverlayInfo(
"com.dummy.overlay_a",
@@ -91,7 +91,7 @@
STATE_DISABLED,
1,
0,
- false);
+ true);
private static final OverlayInfo OVERLAY_B1 = new OverlayInfo(
"com.dummy.overlay_b",
@@ -102,7 +102,7 @@
STATE_DISABLED,
1,
0,
- false);
+ true);
@Before
public void setUp() throws Exception {
@@ -238,7 +238,7 @@
STATE_DISABLED,
0,
0,
- false);
+ true);
insert(otherTarget);
changed = mSettings.setPriority(OVERLAY_A0.packageName, otherTarget.packageName,
OVERLAY_A0.userId);
@@ -435,7 +435,7 @@
private void insert(OverlayInfo oi) throws Exception {
mSettings.init(oi.packageName, oi.userId, oi.targetPackageName, null, oi.baseCodePath,
- false, 0, oi.category);
+ true, false,0, oi.category);
mSettings.setState(oi.packageName, oi.userId, oi.state);
mSettings.setEnabled(oi.packageName, oi.userId, false);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 85840e1..5c15322 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -118,15 +118,15 @@
String[] partitions = { "system", "vendor", "odm", "oem", "product", "system_ext" };
String[] appdir = { "app", "priv-app" };
for (int i = 0; i < partitions.length; i++) {
- final PackageManagerService.SystemPartition systemPartition =
+ final PackageManagerService.ScanPartition scanPartition =
PackageManagerService.SYSTEM_PARTITIONS.get(i);
for (int j = 0; j < appdir.length; j++) {
String canonical = new File("/" + partitions[i]).getCanonicalPath();
String path = String.format("%s/%s/A.apk", canonical, appdir[j]);
- Assert.assertEquals(j == 1 && i != 3, systemPartition.containsPrivPath(path));
+ Assert.assertEquals(j == 1 && i != 3, scanPartition.containsPrivPath(path));
- final int scanFlag = systemPartition.scanFlag;
+ final int scanFlag = scanPartition.scanFlag;
Assert.assertEquals(i == 1, scanFlag == PackageManagerService.SCAN_AS_VENDOR);
Assert.assertEquals(i == 2, scanFlag == PackageManagerService.SCAN_AS_ODM);
Assert.assertEquals(i == 3, scanFlag == PackageManagerService.SCAN_AS_OEM);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index e5ffb4d..604fcd3 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2902,21 +2902,21 @@
}
@Test
- public void testSetListenerAccess_doesNothingOnLowRam() throws Exception {
+ public void testSetListenerAccess_onLowRam() throws Exception {
when(mActivityManager.isLowRamDevice()).thenReturn(true);
ComponentName c = ComponentName.unflattenFromString("package/Component");
mBinderService.setNotificationListenerAccessGranted(c, true);
- verify(mListeners, never()).setPackageOrComponentEnabled(
+ verify(mListeners).setPackageOrComponentEnabled(
anyString(), anyInt(), anyBoolean(), anyBoolean());
- verify(mConditionProviders, never()).setPackageOrComponentEnabled(
+ verify(mConditionProviders).setPackageOrComponentEnabled(
anyString(), anyInt(), anyBoolean(), anyBoolean());
- verify(mAssistants, never()).setPackageOrComponentEnabled(
- any(), anyInt(), anyBoolean(), anyBoolean());
+ verify(mAssistants).migrateToXml();
+ verify(mAssistants).resetDefaultAssistantsIfNecessary();
}
@Test
- public void testSetAssistantAccess_doesNothingOnLowRam() throws Exception {
+ public void testSetAssistantAccess_onLowRam() throws Exception {
when(mActivityManager.isLowRamDevice()).thenReturn(true);
ComponentName c = ComponentName.unflattenFromString("package/Component");
List<UserInfo> uis = new ArrayList<>();
@@ -2927,26 +2927,28 @@
mBinderService.setNotificationAssistantAccessGranted(c, true);
- verify(mListeners, never()).setPackageOrComponentEnabled(
+ verify(mListeners).migrateToXml();
+ verify(mListeners).notifyNotificationChannelChanged(anyString(), any(), any(),
+ anyInt());
+ verify(mConditionProviders).setPackageOrComponentEnabled(
anyString(), anyInt(), anyBoolean(), anyBoolean());
- verify(mConditionProviders, never()).setPackageOrComponentEnabled(
- anyString(), anyInt(), anyBoolean(), anyBoolean());
- verify(mAssistants, never()).setPackageOrComponentEnabled(
- any(), anyInt(), anyBoolean(), anyBoolean());
+ verify(mAssistants).migrateToXml();
+ verify(mAssistants).resetDefaultAssistantsIfNecessary();
}
@Test
- public void testSetDndAccess_doesNothingOnLowRam() throws Exception {
+ public void testSetDndAccess_onLowRam() throws Exception {
when(mActivityManager.isLowRamDevice()).thenReturn(true);
ComponentName c = ComponentName.unflattenFromString("package/Component");
mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
- verify(mListeners, never()).setPackageOrComponentEnabled(
+ verify(mListeners).migrateToXml();
+ verify(mListeners).notifyNotificationChannelChanged(anyString(), any(), any(),
+ anyInt());
+ verify(mConditionProviders).setPackageOrComponentEnabled(
anyString(), anyInt(), anyBoolean(), anyBoolean());
- verify(mConditionProviders, never()).setPackageOrComponentEnabled(
- anyString(), anyInt(), anyBoolean(), anyBoolean());
- verify(mAssistants, never()).setPackageOrComponentEnabled(
- any(), anyInt(), anyBoolean(), anyBoolean());
+ verify(mAssistants).migrateToXml();
+ verify(mAssistants).resetDefaultAssistantsIfNecessary();
}
@Test
@@ -4288,68 +4290,13 @@
}
@Test
- public void testCanUseManagedServicesLowRamNoWatchNullPkg() {
- when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
- when(mActivityManager.isLowRamDevice()).thenReturn(true);
- when(mResources.getStringArray(R.array.config_allowedManagedServicesOnLowRamDevices))
- .thenReturn(new String[] {"a", "b", "c"});
- when(mContext.getResources()).thenReturn(mResources);
-
- assertEquals(false, mService.canUseManagedServices(null, 0, null));
+ public void testCanUseManagedServicesNullPkg() {
+ assertEquals(true, mService.canUseManagedServices(null, 0, null));
}
- @Test
- public void testCanUseManagedServicesLowRamNoWatchValidPkg() {
- when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
- when(mActivityManager.isLowRamDevice()).thenReturn(true);
- when(mResources.getStringArray(R.array.config_allowedManagedServicesOnLowRamDevices))
- .thenReturn(new String[] {"a", "b", "c"});
- when(mContext.getResources()).thenReturn(mResources);
-
- assertEquals(true, mService.canUseManagedServices("b", 0, null));
- }
@Test
- public void testCanUseManagedServicesLowRamNoWatchNoValidPkg() {
- when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
- when(mActivityManager.isLowRamDevice()).thenReturn(true);
- when(mResources.getStringArray(R.array.config_allowedManagedServicesOnLowRamDevices))
- .thenReturn(new String[] {"a", "b", "c"});
- when(mContext.getResources()).thenReturn(mResources);
-
- assertEquals(false, mService.canUseManagedServices("d", 0, null));
- }
-
- @Test
- public void testCanUseManagedServicesLowRamWatchNoValidPkg() {
- when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
- when(mActivityManager.isLowRamDevice()).thenReturn(true);
- when(mResources.getStringArray(R.array.config_allowedManagedServicesOnLowRamDevices))
- .thenReturn(new String[] {"a", "b", "c"});
- when(mContext.getResources()).thenReturn(mResources);
-
- assertEquals(true, mService.canUseManagedServices("d", 0, null));
- }
-
- @Test
- public void testCanUseManagedServicesNoLowRamNoWatchValidPkg() {
- when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
- when(mActivityManager.isLowRamDevice()).thenReturn(false);
- when(mResources.getStringArray(R.array.config_allowedManagedServicesOnLowRamDevices))
- .thenReturn(new String[] {"a", "b", "c"});
- when(mContext.getResources()).thenReturn(mResources);
-
- assertEquals(true, mService.canUseManagedServices("d", 0 , null));
- }
-
- @Test
- public void testCanUseManagedServicesNoLowRamWatchValidPkg() {
- when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
- when(mActivityManager.isLowRamDevice()).thenReturn(false);
- when(mResources.getStringArray(R.array.config_allowedManagedServicesOnLowRamDevices))
- .thenReturn(new String[] {"a", "b", "c"});
- when(mContext.getResources()).thenReturn(mResources);
-
+ public void testCanUseManagedServicesNoValidPkg() {
assertEquals(true, mService.canUseManagedServices("d", 0, null));
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
index 0ea53fa..d765042 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
@@ -22,9 +22,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -32,15 +30,12 @@
import android.app.PendingIntent;
import android.app.Person;
import android.app.RemoteInput;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Icon;
import android.net.Uri;
-import android.os.Build;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.StyleSpan;
@@ -74,91 +69,13 @@
}
@Test
- public void testStripsExtendersInLowRamModeNoWhitelistNoTv() {
+ public void testDoesNotStripsExtenders() {
Notification.Builder nb = new Notification.Builder(mContext, "channel");
nb.extend(new Notification.CarExtender().setColor(Color.RED));
nb.extend(new Notification.TvExtender().setChannelId("different channel"));
nb.extend(new Notification.WearableExtender().setDismissalId("dismiss"));
Notification before = nb.build();
-
- // No whitelist
- Context context = spy(getContext());
- when(context.getResources()).thenReturn(mResources);
- when(mResources.getStringArray(anyInt())).thenReturn(new String[0]);
-
- Notification after = Notification.Builder.maybeCloneStrippedForDelivery(before, true,
- context);
-
- assertEquals("different channel", new Notification.TvExtender(before).getChannelId());
- assertNull(new Notification.TvExtender(after).getChannelId());
-
- assertEquals(Color.RED, new Notification.CarExtender(before).getColor());
- assertEquals(Notification.COLOR_DEFAULT, new Notification.CarExtender(after).getColor());
-
- assertEquals("dismiss", new Notification.WearableExtender(before).getDismissalId());
- assertNull(new Notification.WearableExtender(after).getDismissalId());
- }
-
- @Test
- public void testStripsExtendersInLowRamModeHasWhitelist() {
- Notification.Builder nb = new Notification.Builder(mContext, "channel");
- nb.extend(new Notification.CarExtender().setColor(Color.RED));
- nb.extend(new Notification.TvExtender().setChannelId("different channel"));
- nb.extend(new Notification.WearableExtender().setDismissalId("dismiss"));
- Notification before = nb.build();
-
- // Has whitelist
- Context context = spy(mContext);
- when(context.getResources()).thenReturn(mResources);
- when(mResources.getStringArray(anyInt())).thenReturn(new String[1]);
-
- Notification after = Notification.Builder.maybeCloneStrippedForDelivery(before, true,
- context);
-
- assertEquals("different channel", new Notification.TvExtender(before).getChannelId());
- assertEquals("different channel", new Notification.TvExtender(after).getChannelId());
-
- assertEquals(Color.RED, new Notification.CarExtender(before).getColor());
- assertEquals(Color.RED, new Notification.CarExtender(after).getColor());
-
- assertEquals("dismiss", new Notification.WearableExtender(before).getDismissalId());
- assertEquals("dismiss", new Notification.WearableExtender(after).getDismissalId());
- }
-
- @Test
- public void testStripsRemoteViewsInLowRamMode() {
- Context context = spy(mContext);
- ApplicationInfo ai = new ApplicationInfo();
- ai.targetSdkVersion = Build.VERSION_CODES.M;
- when(context.getApplicationInfo()).thenReturn(ai);
-
- final Notification.BigTextStyle style = new Notification.BigTextStyle()
- .bigText("hello")
- .setSummaryText("And the summary");
- Notification before = new Notification.Builder(context, "channel")
- .setContentText("hi")
- .setStyle(style)
- .build();
-
- Notification after = Notification.Builder.maybeCloneStrippedForDelivery(before, true,
- mContext);
- assertNotNull(before.contentView);
- assertNotNull(before.bigContentView);
- assertNotNull(before.headsUpContentView);
- assertNull(after.contentView);
- assertNull(after.bigContentView);
- assertNull(after.headsUpContentView);
- }
-
- @Test
- public void testDoesNotStripsExtendersInNormalRamMode() {
- Notification.Builder nb = new Notification.Builder(mContext, "channel");
- nb.extend(new Notification.CarExtender().setColor(Color.RED));
- nb.extend(new Notification.TvExtender().setChannelId("different channel"));
- nb.extend(new Notification.WearableExtender().setDismissalId("dismiss"));
- Notification before = nb.build();
- Notification after = Notification.Builder.maybeCloneStrippedForDelivery(before, false,
- mContext);
+ Notification after = Notification.Builder.maybeCloneStrippedForDelivery(before);
assertTrue(before == after);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index fa182d6..c110a0c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -372,7 +372,7 @@
doReturn(mMockPackageManager).when(mService).getPackageManagerInternalLocked();
doReturn(false).when(mMockPackageManager).isInstantAppInstallerComponent(any());
doReturn(null).when(mMockPackageManager).resolveIntent(any(), any(), anyInt(), anyInt(),
- anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), anyInt());
// Never review permissions
doReturn(false).when(mMockPackageManager).isPermissionsReviewRequired(any(), anyInt());
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 70e5ee7..683fca4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -209,6 +209,7 @@
}
@Test
+ @FlakyTest(bugId = 149760957)
public void testSizeCompatBounds() {
// Disable the real configuration resolving because we only simulate partial flow.
// TODO: Have test use full flow.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 8ac1d24..cc9173a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -40,6 +40,8 @@
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
+import androidx.test.filters.FlakyTest;
+
import org.hamcrest.CustomTypeSafeMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
@@ -65,6 +67,7 @@
private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy(null, null);
@Test
+ @FlakyTest(bugId = 149760939)
public void testBuilder() {
WindowManagerService wms = mSystemServices.getWindowManagerService();
DisplayArea.Root root = new SurfacelessDisplayAreaRoot(wms);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index c19312d..ba57745 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -599,6 +599,7 @@
}
@Test
+ @FlakyTest(bugId = 149760800)
public void layoutWindowLw_withLongEdgeDisplayCutout() {
addLongEdgeDisplayCutout();
@@ -618,6 +619,7 @@
}
@Test
+ @FlakyTest(bugId = 149760800)
public void layoutWindowLw_withLongEdgeDisplayCutout_never() {
addLongEdgeDisplayCutout();
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index eda1fb8..b5663bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -255,4 +255,9 @@
int priority) {
return this;
}
+
+ @Override
+ public SurfaceControl.Transaction unsetColor(SurfaceControl sc) {
+ return this;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 8ad7505..55d12db 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -137,6 +137,7 @@
}
throw t;
}
+ if (throwable != null) throw throwable;
}
}
};
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRuleTest.java
new file mode 100644
index 0000000..4056c71
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRuleTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static junit.framework.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runners.model.Statement;
+
+import java.io.IOException;
+
+@Presubmit
+public class SystemServicesTestRuleTest {
+ @Rule
+ public ExpectedException mExpectedException = ExpectedException.none();
+
+ @Test
+ public void testRule_rethrows_unchecked_exceptions() throws Throwable {
+ final SystemServicesTestRule mWmsRule = new SystemServicesTestRule();
+ Statement statement = new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ throw new RuntimeException("A failing test!");
+ }
+ };
+ mExpectedException.expect(RuntimeException.class);
+ mWmsRule.apply(statement, null /* Description*/).evaluate();
+ }
+
+ @Test
+ public void testRule_rethrows_checked_exceptions() throws Throwable {
+ final SystemServicesTestRule mWmsRule = new SystemServicesTestRule();
+ Statement statement = new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ throw new IOException("A failing test!");
+ }
+ };
+ mExpectedException.expect(IOException.class);
+ mWmsRule.apply(statement, null /* Description*/).evaluate();
+ }
+
+ @Test
+ public void testRule_ranSuccessfully() throws Throwable {
+ final boolean[] testRan = {false};
+ final SystemServicesTestRule mWmsRule = new SystemServicesTestRule();
+ Statement statement = new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ testRan[0] = true;
+ }
+ };
+ mWmsRule.apply(statement, null /* Description*/).evaluate();
+ assertTrue(testRan[0]);
+ }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index c51a852..795de57 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3391,6 +3391,25 @@
"subscription_group_uuid_string";
/**
+ * Data switch validation minimal gap time, in milliseconds.
+ *
+ * Which means, if the same subscription on the same network (based on MCC+MNC+TAC+subId)
+ * was recently validated (within this time gap), and Telephony receives a request to switch to
+ * it again, Telephony will skip the validation part and switch to it as soon as connection
+ * is setup, as if it's already validated.
+ *
+ * If the network was validated within the gap but the latest validation result is false, the
+ * validation will not be skipped.
+ *
+ * If not set or set to 0, validation will never be skipped.
+ * The max acceptable value of this config is 24 hours.
+ *
+ * @hide
+ */
+ public static final String KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG =
+ "data_switch_validation_min_gap_LONG";
+
+ /**
* A boolean property indicating whether this subscription should be managed as an opportunistic
* subscription.
*
@@ -4339,6 +4358,7 @@
sDefaults.putAll(Wifi.getDefaults());
sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false);
+ sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, 0);
sDefaults.putAll(Iwlan.getDefaults());
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 957683e..0886975 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -204,6 +204,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.security.KeyStore;
import android.system.Os;
import android.test.mock.MockContentResolver;
import android.text.TextUtils;
@@ -1019,7 +1020,7 @@
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
- userId);
+ userId, mock(KeyStore.class));
}
public void setNetworkAgent(TestNetworkAgentWrapper agent) {
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index ac1c518..0e3b797 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -72,6 +72,7 @@
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyStore;
import android.util.ArrayMap;
@@ -260,17 +261,17 @@
assertFalse(vpn.getLockdown());
// Set always-on without lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList()));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList(), mKeyStore));
assertTrue(vpn.getAlwaysOn());
assertFalse(vpn.getLockdown());
// Set always-on with lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList()));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList(), mKeyStore));
assertTrue(vpn.getAlwaysOn());
assertTrue(vpn.getLockdown());
// Remove always-on configuration.
- assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList()));
+ assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList(), mKeyStore));
assertFalse(vpn.getAlwaysOn());
assertFalse(vpn.getLockdown());
}
@@ -284,11 +285,11 @@
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
// Set always-on without lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null, mKeyStore));
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
// Set always-on with lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null, mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -297,7 +298,7 @@
assertUnblocked(vpn, user.start + PKG_UIDS[1]);
// Switch to another app.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -316,7 +317,8 @@
final UidRange user = UidRange.createForUser(primaryUser.id);
// Set always-on with lockdown and whitelist app PKGS[2] from lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[2])));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[1], true, Collections.singletonList(PKGS[2]), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
@@ -325,7 +327,8 @@
assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
// Change whitelisted app to PKGS[3].
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[3])));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
}));
@@ -337,7 +340,8 @@
assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[3]);
// Change the VPN app.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[3])));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Collections.singletonList(PKGS[3]), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
@@ -350,7 +354,7 @@
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
// Remove the whitelist.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
@@ -363,7 +367,8 @@
assertUnblocked(vpn, user.start + PKG_UIDS[0]);
// Add the whitelist.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[1])));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Collections.singletonList(PKGS[1]), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start + PKG_UIDS[0] + 1, user.stop)
}));
@@ -375,12 +380,13 @@
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]);
// Try whitelisting a package with a comma, should be rejected.
- assertFalse(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList("a.b,c.d")));
+ assertFalse(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Collections.singletonList("a.b,c.d"), mKeyStore));
// Pass a non-existent packages in the whitelist, they (and only they) should be ignored.
// Whitelisted package should change from PGKS[1] to PKGS[2].
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true,
- Arrays.asList("com.foo.app", PKGS[2], "com.bar.app")));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[]{
new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -405,7 +411,7 @@
final UidRange profile = UidRange.createForUser(tempProfile.id);
// Set lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
@@ -499,22 +505,22 @@
.thenReturn(Collections.singletonList(resInfo));
// null package name should return false
- assertFalse(vpn.isAlwaysOnPackageSupported(null));
+ assertFalse(vpn.isAlwaysOnPackageSupported(null, mKeyStore));
// Pre-N apps are not supported
appInfo.targetSdkVersion = VERSION_CODES.M;
- assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+ assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
// N+ apps are supported by default
appInfo.targetSdkVersion = VERSION_CODES.N;
- assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+ assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
// Apps that opt out explicitly are not supported
appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
Bundle metaData = new Bundle();
metaData.putBoolean(VpnService.SERVICE_META_DATA_SUPPORTS_ALWAYS_ON, false);
svcInfo.metaData = metaData;
- assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+ assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
}
@Test
@@ -531,7 +537,7 @@
.cancelAsUser(anyString(), anyInt(), eq(userHandle));
// Start showing a notification for disconnected once always-on.
- vpn.setAlwaysOnPackage(PKGS[0], false, null);
+ vpn.setAlwaysOnPackage(PKGS[0], false, null, mKeyStore);
order.verify(mNotificationManager)
.notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
@@ -545,7 +551,7 @@
.notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
// Notification should be cleared after unsetting always-on package.
- vpn.setAlwaysOnPackage(null, false, null);
+ vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle));
}
@@ -920,12 +926,48 @@
eq(AppOpsManager.MODE_IGNORED));
}
+ private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) {
+ assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, lockdownEnabled, null, mKeyStore));
+
+ verify(mKeyStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ verify(mAppOps).setMode(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN), eq(uid), eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_ALLOWED));
+
+ verify(mSystemServices).settingsSecurePutStringForUser(
+ eq(Settings.Secure.ALWAYS_ON_VPN_APP), eq(TEST_VPN_PKG), eq(primaryUser.id));
+ verify(mSystemServices).settingsSecurePutIntForUser(
+ eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN), eq(lockdownEnabled ? 1 : 0),
+ eq(primaryUser.id));
+ verify(mSystemServices).settingsSecurePutStringForUser(
+ eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST), eq(""), eq(primaryUser.id));
+ }
+
+ @Test
+ public void testSetAndStartAlwaysOnVpn() throws Exception {
+ final Vpn vpn = createVpn(primaryUser.id);
+ setMockedUsers(primaryUser);
+
+ // UID checks must return a different UID; otherwise it'll be treated as already prepared.
+ final int uid = Process.myUid() + 1;
+ when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
+ .thenReturn(uid);
+ when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ setAndVerifyAlwaysOnPackage(vpn, uid, false);
+ assertTrue(vpn.startAlwaysOnVpn(mKeyStore));
+
+ // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
+ // a subsequent CL.
+ }
+
/**
* Mock some methods of vpn object.
*/
private Vpn createVpn(@UserIdInt int userId) {
return new Vpn(Looper.myLooper(), mContext, mNetService,
- userId, mSystemServices, mIkev2SessionCreator);
+ userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
}
private static void assertBlocked(Vpn vpn, int... uids) {
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index a251c05..e566d27 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -123,5 +123,12 @@
"libcutils",
],
static_libs: ["libstatssocket"],
+
+ apex_available: [
+ "//apex_available:platform",
+ //TODO(b/149781190): Remove this once statsd no longer depends on libstatslog
+ "com.android.os.statsd",
+ "test_com.android.os.statsd",
+ ],
}