Merge "Add permission hint, update permission slice structure" into pi-dev
diff --git a/Android.mk b/Android.mk
index e219661..d7d9c90 100644
--- a/Android.mk
+++ b/Android.mk
@@ -252,6 +252,11 @@
-federate SupportLib https://developer.android.com \
-federationapi SupportLib prebuilts/sdk/current/support-api.txt
+# Federate AndroidX references against local API file.
+framework_docs_LOCAL_DROIDDOC_OPTIONS += \
+ -federate AndroidX https://developer.android.com \
+ -federationapi AndroidX prebuilts/sdk/current/androidx-api.txt
+
# ==== Public API diff ===========================
include $(CLEAR_VARS)
diff --git a/api/current.txt b/api/current.txt
index 7251e8f..9968258 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6115,8 +6115,12 @@
method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
method public android.view.WindowContentFrameStats getWindowContentFrameStats(int);
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
+ method public void grantRuntimePermission(java.lang.String, java.lang.String);
+ method public void grantRuntimePermissionAsUser(java.lang.String, java.lang.String, android.os.UserHandle);
method public boolean injectInputEvent(android.view.InputEvent, boolean);
method public boolean performGlobalAction(int);
+ method public void revokeRuntimePermission(java.lang.String, java.lang.String);
+ method public void revokeRuntimePermissionAsUser(java.lang.String, java.lang.String, android.os.UserHandle);
method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
method public boolean setRotation(int);
method public void setRunAsMonkey(boolean);
@@ -6501,7 +6505,7 @@
method public boolean installExistingPackage(android.content.ComponentName, java.lang.String);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
- method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean, boolean);
+ method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, int);
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isAffiliatedUser();
@@ -6685,6 +6689,8 @@
field public static final int ID_TYPE_IMEI = 4; // 0x4
field public static final int ID_TYPE_MEID = 8; // 0x8
field public static final int ID_TYPE_SERIAL = 2; // 0x2
+ field public static final int INSTALLKEY_REQUEST_CREDENTIALS_ACCESS = 1; // 0x1
+ field public static final int INSTALLKEY_SET_USER_SELECTABLE = 2; // 0x2
field public static final int KEYGUARD_DISABLE_BIOMETRICS = 416; // 0x1a0
field public static final int KEYGUARD_DISABLE_FACE = 128; // 0x80
field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff
@@ -13645,7 +13651,6 @@
method public android.graphics.ImageDecoder.OnPartialImageListener getOnPartialImageListener();
method public android.graphics.PostProcessor getPostProcessor();
method public boolean getRequireUnpremultiplied();
- method public android.util.Size getSampledSize(int);
method public android.graphics.ImageDecoder setAllocator(int);
method public android.graphics.ImageDecoder setConserveMemory(boolean);
method public android.graphics.ImageDecoder setCrop(android.graphics.Rect);
@@ -13654,8 +13659,9 @@
method public android.graphics.ImageDecoder setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener);
method public android.graphics.ImageDecoder setPostProcessor(android.graphics.PostProcessor);
method public android.graphics.ImageDecoder setRequireUnpremultiplied(boolean);
- method public android.graphics.ImageDecoder setResize(int, int);
- method public android.graphics.ImageDecoder setResize(int);
+ method public android.graphics.ImageDecoder setTargetColorSpace(android.graphics.ColorSpace);
+ method public android.graphics.ImageDecoder setTargetSampleSize(int);
+ method public android.graphics.ImageDecoder setTargetSize(int, int);
field public static final int ALLOCATOR_DEFAULT = 0; // 0x0
field public static final int ALLOCATOR_HARDWARE = 3; // 0x3
field public static final int ALLOCATOR_SHARED_MEMORY = 2; // 0x2
@@ -13671,6 +13677,7 @@
}
public static class ImageDecoder.ImageInfo {
+ method public android.graphics.ColorSpace getColorSpace();
method public java.lang.String getMimeType();
method public android.util.Size getSize();
method public boolean isAnimated();
@@ -23455,9 +23462,6 @@
field public static final int HDCP_V2 = 3; // 0x3
field public static final int HDCP_V2_1 = 4; // 0x4
field public static final int HDCP_V2_2 = 5; // 0x5
- field public static final int HW_SECURE_ALL = 5; // 0x5
- field public static final int HW_SECURE_CRYPTO = 3; // 0x3
- field public static final int HW_SECURE_DECODE = 4; // 0x4
field public static final int KEY_TYPE_OFFLINE = 2; // 0x2
field public static final int KEY_TYPE_RELEASE = 3; // 0x3
field public static final int KEY_TYPE_STREAMING = 1; // 0x1
@@ -23466,9 +23470,12 @@
field public static final java.lang.String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
field public static final java.lang.String PROPERTY_VENDOR = "vendor";
field public static final java.lang.String PROPERTY_VERSION = "version";
+ field public static final int SECURITY_LEVEL_HW_SECURE_ALL = 5; // 0x5
+ field public static final int SECURITY_LEVEL_HW_SECURE_CRYPTO = 3; // 0x3
+ field public static final int SECURITY_LEVEL_HW_SECURE_DECODE = 4; // 0x4
+ field public static final int SECURITY_LEVEL_SW_SECURE_CRYPTO = 1; // 0x1
+ field public static final int SECURITY_LEVEL_SW_SECURE_DECODE = 2; // 0x2
field public static final int SECURITY_LEVEL_UNKNOWN = 0; // 0x0
- field public static final int SW_SECURE_CRYPTO = 1; // 0x1
- field public static final int SW_SECURE_DECODE = 2; // 0x2
}
public final class MediaDrm.CryptoSession {
@@ -25759,6 +25766,7 @@
public final class MediaSession {
ctor public MediaSession(android.content.Context, java.lang.String);
method public android.media.session.MediaController getController();
+ method public android.media.session.MediaSessionManager.RemoteUserInfo getCurrentControllerInfo();
method public android.media.session.MediaSession.Token getSessionToken();
method public boolean isActive();
method public void release();
@@ -25825,6 +25833,7 @@
method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName);
method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler);
method public java.util.List<android.media.session.MediaController> getActiveSessions(android.content.ComponentName);
+ method public boolean isTrustedForMediaControl(android.media.session.MediaSessionManager.RemoteUserInfo);
method public void removeOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
}
@@ -25832,6 +25841,13 @@
method public abstract void onActiveSessionsChanged(java.util.List<android.media.session.MediaController>);
}
+ public static final class MediaSessionManager.RemoteUserInfo {
+ ctor public MediaSessionManager.RemoteUserInfo(java.lang.String, int, int);
+ method public java.lang.String getPackageName();
+ method public int getPid();
+ method public int getUid();
+ }
+
public final class PlaybackState implements android.os.Parcelable {
method public int describeContents();
method public long getActions();
@@ -39169,6 +39185,7 @@
ctor public MediaBrowserService();
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public final android.os.Bundle getBrowserRootHints();
+ method public final android.media.session.MediaSessionManager.RemoteUserInfo getCurrentBrowserInfo();
method public android.media.session.MediaSession.Token getSessionToken();
method public void notifyChildrenChanged(java.lang.String);
method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
@@ -50403,9 +50420,9 @@
ctor public TextClassification.Options();
method public int describeContents();
method public android.os.LocaleList getDefaultLocales();
- method public java.util.Calendar getReferenceTime();
+ method public java.time.ZonedDateTime getReferenceTime();
method public android.view.textclassifier.TextClassification.Options setDefaultLocales(android.os.LocaleList);
- method public android.view.textclassifier.TextClassification.Options setReferenceTime(java.util.Calendar);
+ method public android.view.textclassifier.TextClassification.Options setReferenceTime(java.time.ZonedDateTime);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassification.Options> CREATOR;
}
diff --git a/api/removed.txt b/api/removed.txt
index 9fe25c9..5863e77 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -184,6 +184,8 @@
public final class ImageDecoder implements java.lang.AutoCloseable {
method public deprecated boolean getAsAlphaMask();
method public deprecated android.graphics.ImageDecoder setAsAlphaMask(boolean);
+ method public deprecated android.graphics.ImageDecoder setResize(int, int);
+ method public deprecated android.graphics.ImageDecoder setResize(int);
field public static final deprecated int ERROR_SOURCE_ERROR = 3; // 0x3
field public static final deprecated int ERROR_SOURCE_EXCEPTION = 1; // 0x1
field public static final deprecated int ERROR_SOURCE_INCOMPLETE = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 5568dba..ed763ba 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -57,6 +57,7 @@
field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
field public static final java.lang.String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
field public static final java.lang.String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
+ field public static final java.lang.String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION";
field public static final java.lang.String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE";
field public static final java.lang.String CONTROL_LOCATION_UPDATES = "android.permission.CONTROL_LOCATION_UPDATES";
field public static final java.lang.String CONTROL_VPN = "android.permission.CONTROL_VPN";
@@ -1252,6 +1253,7 @@
method public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
method public android.graphics.Point getStableDisplaySize();
method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
+ method public void setSaturationLevel(float);
}
}
@@ -2101,6 +2103,7 @@
method public java.lang.String getVersion();
method public boolean isBackgroundScanningSupported();
method public boolean isCaptureSupported();
+ method public boolean isInitializationRequired();
method public boolean isProgramIdentifierSupported(int);
method public boolean isProgramTypeSupported(int);
method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index d323725..b520dfb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1,8 +1,10 @@
package android {
public static final class Manifest.permission {
+ field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
+ field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
}
}
@@ -127,8 +129,8 @@
public final class UiAutomation {
method public void destroy();
method public android.os.ParcelFileDescriptor[] executeShellCommandRw(java.lang.String);
- method public boolean grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
- method public boolean revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+ method public deprecated boolean grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+ method public deprecated boolean revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
}
public class UiModeManager {
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index 9df229c..6676196 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -264,13 +264,13 @@
} else if ("q".equals(line) || "quit".equals(line)) {
break;
} else if ("play".equals(line)) {
- mController.play();
+ mController.play("");
} else if ("pause".equals(line)) {
- mController.pause();
+ mController.pause("");
} else if ("next".equals(line)) {
- mController.next();
+ mController.next("");
} else if ("previous".equals(line)) {
- mController.previous();
+ mController.previous("");
} else {
System.out.println("Invalid command: " + line);
}
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index a458c07..c1ff275 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -79,8 +79,6 @@
mSendBroadcast(sendBroadcast),
mTimeBaseSec(timeBaseSec),
mLastLogTimestamp(0) {
- StatsPullerManager statsPullerManager;
- statsPullerManager.SetTimeBaseSec(mTimeBaseSec);
}
StatsLogProcessor::~StatsLogProcessor() {
@@ -177,7 +175,7 @@
uint64_t curTimeSec = getElapsedRealtimeSec();
if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) {
- mStatsPullerManager.ClearPullerCacheIfNecessary(curTimeSec);
+ mStatsPullerManager.ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC);
mLastPullerCacheClearTimeSec = curTimeSec;
}
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index b03b4b4..9f70c75 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -595,7 +595,7 @@
status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
int s = atoi(args[1].c_str());
vector<shared_ptr<LogEvent> > stats;
- if (mStatsPullerManager.Pull(s, &stats)) {
+ if (mStatsPullerManager.Pull(s, getElapsedRealtimeNs(), &stats)) {
for (const auto& it : stats) {
fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5e75359..cfb9d87 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -245,7 +245,7 @@
* Logs when the ble scan state changes.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java
*/
// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
message BleScanStateChanged {
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 3b0cd34..ec5a5d6 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -35,26 +35,31 @@
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
StatsPuller::StatsPuller(const int tagId)
: mTagId(tagId) {
- mCoolDownSec = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.coolDownSec;
- VLOG("Puller for tag %d created. Cooldown set to %ld", mTagId, mCoolDownSec);
+ mCoolDownNs = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
+ VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
}
-bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
+bool StatsPuller::Pull(const int64_t elapsedTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) {
lock_guard<std::mutex> lock(mLock);
+ int64_t wallClockTimeNs = getWallClockNs();
StatsdStats::getInstance().notePull(mTagId);
- long curTime = getElapsedRealtimeSec();
- if (curTime - mLastPullTimeSec < mCoolDownSec) {
+ if (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs) {
(*data) = mCachedData;
StatsdStats::getInstance().notePullFromCache(mTagId);
return true;
}
- if (mMinPullIntervalSec > curTime - mLastPullTimeSec) {
- mMinPullIntervalSec = curTime - mLastPullTimeSec;
- StatsdStats::getInstance().updateMinPullIntervalSec(mTagId, mMinPullIntervalSec);
+ if (mMinPullIntervalNs > elapsedTimeNs - mLastPullTimeNs) {
+ mMinPullIntervalNs = elapsedTimeNs - mLastPullTimeNs;
+ StatsdStats::getInstance().updateMinPullIntervalSec(mTagId,
+ mMinPullIntervalNs / NS_PER_SEC);
}
mCachedData.clear();
- mLastPullTimeSec = curTime;
+ mLastPullTimeNs = elapsedTimeNs;
bool ret = PullInternal(&mCachedData);
+ for (const shared_ptr<LogEvent>& data : mCachedData) {
+ data->setElapsedTimestampNs(elapsedTimeNs);
+ data->setLogdWallClockTimestampNs(wallClockTimeNs);
+ }
if (ret) {
mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
(*data) = mCachedData;
@@ -70,12 +75,12 @@
lock_guard<std::mutex> lock(mLock);
int ret = mCachedData.size();
mCachedData.clear();
- mLastPullTimeSec = 0;
+ mLastPullTimeNs = 0;
return ret;
}
-int StatsPuller::ClearCacheIfNecessary(long timestampSec) {
- if (timestampSec - mLastPullTimeSec > mCoolDownSec) {
+int StatsPuller::ClearCacheIfNecessary(int64_t timestampNs) {
+ if (timestampNs - mLastPullTimeNs > mCoolDownNs) {
return clearCache();
} else {
return 0;
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 936c47e..caac677 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -37,13 +37,13 @@
virtual ~StatsPuller() {}
- bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
+ bool Pull(const int64_t timeNs, std::vector<std::shared_ptr<LogEvent>>* data);
// Clear cache immediately
int ForceClearCache();
// Clear cache if elapsed time is more than cooldown time
- int ClearCacheIfNecessary(long timestampSec);
+ int ClearCacheIfNecessary(int64_t timestampNs);
static void SetUidMap(const sp<UidMap>& uidMap);
@@ -59,9 +59,9 @@
// If a pull request comes before cooldown, a cached version from purevious pull
// will be returned.
// The actual value should be determined by individual pullers.
- long mCoolDownSec;
+ int64_t mCoolDownNs;
// For puller stats
- long mMinPullIntervalSec = LONG_MAX;
+ int64_t mMinPullIntervalNs = LONG_MAX;
virtual bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) = 0;
@@ -69,7 +69,7 @@
// cached data will be returned.
std::vector<std::shared_ptr<LogEvent>> mCachedData;
- long mLastPullTimeSec;
+ int64_t mLastPullTimeNs;
int clearCache();
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 2717d5c..83d59c0 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -26,10 +26,9 @@
public:
virtual ~StatsPullerManager() {}
- virtual void RegisterReceiver(int tagId,
- wp <PullDataReceiver> receiver,
- long intervalMs) {
- mPullerManager.RegisterReceiver(tagId, receiver, intervalMs);
+ virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+ int64_t intervalNs) {
+ mPullerManager.RegisterReceiver(tagId, receiver, nextPullTimeNs, intervalNs);
};
virtual void UnRegisterReceiver(int tagId, wp <PullDataReceiver> receiver) {
@@ -45,13 +44,9 @@
mPullerManager.OnAlarmFired();
}
- virtual bool
- Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) {
- return mPullerManager.Pull(tagId, data);
- }
-
- void SetTimeBaseSec(const long timeBaseSec) {
- mPullerManager.SetTimeBaseSec(timeBaseSec);
+ virtual bool Pull(const int tagId, const int64_t timesNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ return mPullerManager.Pull(tagId, timesNs, data);
}
int ForceClearPullerCache() {
@@ -62,8 +57,8 @@
mPullerManager.SetStatsCompanionService(statsCompanionService);
}
- int ClearPullerCacheIfNecessary(long timestampSec) {
- return mPullerManager.ClearPullerCacheIfNecessary(timestampSec);
+ int ClearPullerCacheIfNecessary(int64_t timestampNs) {
+ return mPullerManager.ClearPullerCacheIfNecessary(timestampNs);
}
private:
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index dd6406b..0e23bf0 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -19,15 +19,17 @@
#include <android/os/IStatsCompanionService.h>
#include <cutils/log.h>
+#include <math.h>
#include <algorithm>
#include <climits>
+#include "../StatsService.h"
#include "../logd/LogEvent.h"
#include "../stats_log_util.h"
#include "../statscompanion_util.h"
#include "ResourceHealthManagerPuller.h"
#include "ResourceThermalManagerPuller.h"
#include "StatsCompanionServicePuller.h"
-#include "StatsService.h"
+#include "StatsPullerManagerImpl.h"
#include "SubsystemSleepStatePuller.h"
#include "statslog.h"
@@ -47,89 +49,136 @@
const std::map<int, PullAtomInfo> StatsPullerManagerImpl::kAllPullAtomInfo = {
// wifi_bytes_transfer
{android::util::WIFI_BYTES_TRANSFER,
- {{2, 3, 4, 5}, {}, 1,
+ {{2, 3, 4, 5},
+ {},
+ 1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
// wifi_bytes_transfer_by_fg_bg
{android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
- {{3, 4, 5, 6}, {2}, 1,
+ {{3, 4, 5, 6},
+ {2},
+ 1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
// mobile_bytes_transfer
{android::util::MOBILE_BYTES_TRANSFER,
- {{2, 3, 4, 5}, {}, 1,
+ {{2, 3, 4, 5},
+ {},
+ 1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
// mobile_bytes_transfer_by_fg_bg
{android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
- {{3, 4, 5, 6}, {2}, 1,
+ {{3, 4, 5, 6},
+ {2},
+ 1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
// bluetooth_bytes_transfer
{android::util::BLUETOOTH_BYTES_TRANSFER,
- {{2, 3}, {}, 1, new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
+ {{2, 3},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
// kernel_wakelock
{android::util::KERNEL_WAKELOCK,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
// subsystem_sleep_state
{android::util::SUBSYSTEM_SLEEP_STATE,
- {{}, {}, 1, new SubsystemSleepStatePuller()}},
+ {{}, {}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
// cpu_time_per_freq
{android::util::CPU_TIME_PER_FREQ,
- {{3}, {2}, 1, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+ {{3},
+ {2},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
// cpu_time_per_uid
{android::util::CPU_TIME_PER_UID,
- {{2, 3}, {}, 1, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
+ {{2, 3},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
// cpu_time_per_uid_freq
- // the throttling is 3sec, handled in frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
+ // the throttling is 3sec, handled in
+ // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_TIME_PER_UID_FREQ,
- {{4}, {2,3}, 0, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
+ {{4},
+ {2, 3},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
// cpu_active_time
- // the throttling is 3sec, handled in frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
+ // the throttling is 3sec, handled in
+ // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_ACTIVE_TIME,
- {{2}, {}, 0, new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
+ {{2},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
// cpu_cluster_time
- // the throttling is 3sec, handled in frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
+ // the throttling is 3sec, handled in
+ // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_CLUSTER_TIME,
- {{3}, {2}, 0, new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
+ {{3},
+ {2},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
// wifi_activity_energy_info
{android::util::WIFI_ACTIVITY_ENERGY_INFO,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_ENERGY_INFO)}},
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_ENERGY_INFO)}},
// modem_activity_info
{android::util::MODEM_ACTIVITY_INFO,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
// bluetooth_activity_info
{android::util::BLUETOOTH_ACTIVITY_INFO,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
// system_elapsed_realtime
{android::util::SYSTEM_ELAPSED_REALTIME,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
// system_uptime
{android::util::SYSTEM_UPTIME,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
// disk_space
{android::util::DISK_SPACE,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::DISK_SPACE)}},
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_SPACE)}},
// remaining_battery_capacity
{android::util::REMAINING_BATTERY_CAPACITY,
- {{}, {}, 1, new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
// full_battery_capacity
{android::util::FULL_BATTERY_CAPACITY,
- {{}, {}, 1, new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
// process_memory_state
{android::util::PROCESS_MEMORY_STATE,
- {{4,5,6,7,8},
- {2,3},
- 0,
+ {{4, 5, 6, 7, 8},
+ {2, 3},
+ 1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
// temperature
{android::util::TEMPERATURE, {{}, {}, 1, new ResourceThermalManagerPuller()}}};
-StatsPullerManagerImpl::StatsPullerManagerImpl()
- : mCurrentPullingInterval(LONG_MAX) {
+StatsPullerManagerImpl::StatsPullerManagerImpl() : mNextPullTimeNs(LONG_MAX) {
}
-bool StatsPullerManagerImpl::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
+bool StatsPullerManagerImpl::Pull(const int tagId, const int64_t timeNs,
+ vector<shared_ptr<LogEvent>>* data) {
VLOG("Initiating pulling %d", tagId);
if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) {
- bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data);
+ bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(timeNs, data);
VLOG("pulled %d items", (int)data->size());
return ret;
} else {
@@ -148,12 +197,14 @@
}
void StatsPullerManagerImpl::updateAlarmLocked() {
- long currentTimeMs = getElapsedRealtimeMillis();
- long nextAlarmTimeMs = currentTimeMs + mCurrentPullingInterval -
- (currentTimeMs - mTimeBaseSec * 1000) % mCurrentPullingInterval;
+ if (mNextPullTimeNs == LONG_MAX) {
+ VLOG("No need to set alarms. Skipping");
+ return;
+ }
+
sp<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
if (statsCompanionServiceCopy != nullptr) {
- statsCompanionServiceCopy->setPullingAlarms(nextAlarmTimeMs, mCurrentPullingInterval);
+ statsCompanionServiceCopy->setPullingAlarm(mNextPullTimeNs / 1000000);
} else {
VLOG("StatsCompanionService not available. Alarm not set.");
}
@@ -174,7 +225,7 @@
}
void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
- long intervalMs) {
+ int64_t nextPullTimeNs, int64_t intervalNs) {
AutoMutex _l(mLock);
auto& receivers = mReceivers[tagId];
for (auto it = receivers.begin(); it != receivers.end(); it++) {
@@ -185,21 +236,24 @@
}
ReceiverInfo receiverInfo;
receiverInfo.receiver = receiver;
- receiverInfo.timeInfo.first = intervalMs;
- receivers.push_back(receiverInfo);
// Round it to the nearest minutes. This is the limit of alarm manager.
- // In practice, we should limit it higher.
- long roundedIntervalMs = intervalMs/1000/60 * 1000 * 60;
+ // In practice, we should always have larger buckets.
+ int64_t roundedIntervalNs = intervalNs / NS_PER_SEC / 60 * NS_PER_SEC * 60;
// Scheduled pulling should be at least 1 min apart.
// This can be lower in cts tests, in which case we round it to 1 min.
- if (roundedIntervalMs < 60 * 1000) {
- roundedIntervalMs = 60 * 1000;
+ if (roundedIntervalNs < 60 * (int64_t)NS_PER_SEC) {
+ roundedIntervalNs = 60 * (int64_t)NS_PER_SEC;
}
+
+ receiverInfo.intervalNs = roundedIntervalNs;
+ receiverInfo.nextPullTimeNs = nextPullTimeNs;
+ receivers.push_back(receiverInfo);
+
// There is only one alarm for all pulled events. So only set it to the smallest denom.
- if (roundedIntervalMs < mCurrentPullingInterval) {
- VLOG("Updating pulling interval %ld", intervalMs);
- mCurrentPullingInterval = roundedIntervalMs;
+ if (nextPullTimeNs < mNextPullTimeNs) {
+ VLOG("Updating next pull time %lld", (long long)mNextPullTimeNs);
+ mNextPullTimeNs = nextPullTimeNs;
updateAlarmLocked();
}
VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
@@ -224,16 +278,22 @@
void StatsPullerManagerImpl::OnAlarmFired() {
AutoMutex _l(mLock);
- uint64_t currentTimeMs = getElapsedRealtimeMillis();
+ int64_t currentTimeNs = getElapsedRealtimeNs();
+
+ int64_t minNextPullTimeNs = LONG_MAX;
vector<pair<int, vector<ReceiverInfo*>>> needToPull =
vector<pair<int, vector<ReceiverInfo*>>>();
for (auto& pair : mReceivers) {
vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
if (pair.second.size() != 0) {
- for (auto& receiverInfo : pair.second) {
- if (receiverInfo.timeInfo.first + receiverInfo.timeInfo.second > currentTimeMs) {
+ for (ReceiverInfo& receiverInfo : pair.second) {
+ if (receiverInfo.nextPullTimeNs < currentTimeNs) {
receivers.push_back(&receiverInfo);
+ } else {
+ if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) {
+ minNextPullTimeNs = receiverInfo.nextPullTimeNs;
+ }
}
}
if (receivers.size() > 0) {
@@ -244,18 +304,29 @@
for (const auto& pullInfo : needToPull) {
vector<shared_ptr<LogEvent>> data;
- if (Pull(pullInfo.first, &data)) {
+ if (Pull(pullInfo.first, currentTimeNs, &data)) {
for (const auto& receiverInfo : pullInfo.second) {
sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
if (receiverPtr != nullptr) {
receiverPtr->onDataPulled(data);
- receiverInfo->timeInfo.second = currentTimeMs;
+ // we may have just come out of a coma, compute next pull time
+ receiverInfo->nextPullTimeNs =
+ ceil((double_t)(currentTimeNs - receiverInfo->nextPullTimeNs) /
+ receiverInfo->intervalNs) *
+ receiverInfo->intervalNs +
+ receiverInfo->nextPullTimeNs;
+ if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) {
+ minNextPullTimeNs = receiverInfo->nextPullTimeNs;
+ }
} else {
VLOG("receiver already gone.");
}
}
}
}
+
+ mNextPullTimeNs = minNextPullTimeNs;
+ updateAlarmLocked();
}
int StatsPullerManagerImpl::ForceClearPullerCache() {
@@ -266,10 +337,10 @@
return totalCleared;
}
-int StatsPullerManagerImpl::ClearPullerCacheIfNecessary(long timestampSec) {
+int StatsPullerManagerImpl::ClearPullerCacheIfNecessary(int64_t timestampNs) {
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
- totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampSec);
+ totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampNs);
}
return totalCleared;
}
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 682ad33..8c771f3 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -41,7 +41,7 @@
std::vector<int> nonAdditiveFields;
// How long should the puller wait before doing an actual pull again. Default
// 1 sec. Set this to 0 if this is handled elsewhere.
- long coolDownSec = 1;
+ int64_t coolDownNs = 1 * NS_PER_SEC;
// The actual puller
sp<StatsPuller> puller;
} PullAtomInfo;
@@ -50,7 +50,8 @@
public:
static StatsPullerManagerImpl& GetInstance();
- void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, long intervalMs);
+ void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+ int64_t intervalNs);
void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
@@ -59,13 +60,11 @@
void OnAlarmFired();
- bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data);
-
- void SetTimeBaseSec(long timeBaseSec) {mTimeBaseSec = timeBaseSec;};
+ bool Pull(const int tagId, const int64_t timeNs, vector<std::shared_ptr<LogEvent>>* data);
int ForceClearPullerCache();
- int ClearPullerCacheIfNecessary(long timestampSec);
+ int ClearPullerCacheIfNecessary(int64_t timestampNs);
void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
@@ -77,8 +76,8 @@
sp<IStatsCompanionService> mStatsCompanionService = nullptr;
typedef struct {
- // pull_interval_sec : last_pull_time_sec
- std::pair<uint64_t, uint64_t> timeInfo;
+ int64_t nextPullTimeNs;
+ int64_t intervalNs;
wp<PullDataReceiver> receiver;
} ReceiverInfo;
@@ -90,12 +89,7 @@
void updateAlarmLocked();
- long mCurrentPullingInterval;
-
- // for pulled metrics, it is important for the buckets to be aligned to multiple of smallest
- // bucket size. All pulled metrics start pulling based on this time, so that they can be
- // correctly attributed to the correct buckets.
- long mTimeBaseSec;
+ int64_t mNextPullTimeNs;
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index f0e0df1..b13c3e7 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -112,7 +112,8 @@
// Kicks off the puller immediately.
if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- mStatsPullerManager->RegisterReceiver(mPullTagId, this, bucketSizeMills);
+ mStatsPullerManager->RegisterReceiver(
+ mPullTagId, this, mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
}
VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
@@ -255,7 +256,7 @@
}
vector<std::shared_ptr<LogEvent>> allData;
- if (!mStatsPullerManager->Pull(mPullTagId, &allData)) {
+ if (!mStatsPullerManager->Pull(mPullTagId, getElapsedRealtimeNs(), &allData)) {
ALOGE("Gauge Stats puller failed for tag: %d", mPullTagId);
return;
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index e19e236..bd3c78c 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -110,10 +110,12 @@
}
mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
- if (!metric.has_condition() && mPullTagId != -1) {
- VLOG("Setting up periodic pulling for %d", mPullTagId);
- mStatsPullerManager->RegisterReceiver(mPullTagId, this, bucketSizeMills);
+ // Kicks off the puller immediately.
+ if (mPullTagId != -1) {
+ mStatsPullerManager->RegisterReceiver(
+ mPullTagId, this, mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
}
+
VLOG("value metric %lld created. bucket size %lld start_time: %lld",
(long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -194,26 +196,21 @@
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
-void ValueMetricProducer::onConditionChangedLocked(const bool condition, const uint64_t eventTime) {
+void ValueMetricProducer::onConditionChangedLocked(const bool condition,
+ const uint64_t eventTimeNs) {
mCondition = condition;
- if (eventTime < mCurrentBucketStartTimeNs) {
- VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTime,
+ if (eventTimeNs < mCurrentBucketStartTimeNs) {
+ VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
(long long)mCurrentBucketStartTimeNs);
return;
}
- flushIfNeededLocked(eventTime);
+ flushIfNeededLocked(eventTimeNs);
if (mPullTagId != -1) {
- if (mCondition == true) {
- mStatsPullerManager->RegisterReceiver(mPullTagId, this, mBucketSizeNs / 1000 / 1000);
- } else if (mCondition == false) {
- mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
- }
-
vector<shared_ptr<LogEvent>> allData;
- if (mStatsPullerManager->Pull(mPullTagId, &allData)) {
+ if (mStatsPullerManager->Pull(mPullTagId, eventTimeNs, &allData)) {
if (allData.size() == 0) {
return;
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 796e83a..ebc6e81 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -53,7 +53,7 @@
if (mPullTagId != -1) {
vector<shared_ptr<LogEvent>> allData;
- mStatsPullerManager->Pull(mPullTagId, &allData);
+ mStatsPullerManager->Pull(mPullTagId, eventTimeNs, &allData);
if (allData.size() == 0) {
// This shouldn't happen since this valuemetric is not useful now.
}
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index cab61e9..efd810f 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -221,7 +221,8 @@
int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) {
int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit);
- if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL) {
+ if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL &&
+ uid != AID_ROOT) {
bucketSizeMillis = 5 * 60 * 1000LL;
}
return bucketSizeMillis;
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 2583c95..7ca66fd 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -63,7 +63,7 @@
// For now we still need this so that it doesn't do real pulling.
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
@@ -213,10 +213,11 @@
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventUpgradeTimeNs);
event->write("some value");
@@ -281,10 +282,11 @@
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write("some value");
@@ -372,10 +374,11 @@
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write(1000);
@@ -420,7 +423,7 @@
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
GaugeMetric metric;
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index a8eb2703..a0224ec 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -62,7 +62,7 @@
// For now we still need this so that it doesn't do real pulling.
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
@@ -141,11 +141,12 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write(tagId);
@@ -154,7 +155,8 @@
data->push_back(event);
return true;
}))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
event->write(tagId);
@@ -260,10 +262,11 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write(tagId);
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index f040bf9..5afaba6 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -35,9 +35,11 @@
class MockStatsPullerManager : public StatsPullerManager {
public:
- MOCK_METHOD3(RegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver, long intervalMs));
+ MOCK_METHOD4(RegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver,
+ int64_t nextPulltimeNs, int64_t intervalNs));
MOCK_METHOD2(UnRegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver));
- MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
+ MOCK_METHOD3(Pull, bool(const int pullCode, const int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data));
};
class MockUidMap : public UidMap {
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 3af6105..70e3fad 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1,4 +1,5 @@
Landroid/accounts/AccountManager;->mContext:Landroid/content/Context;
+Landroid/accounts/IAccountAuthenticatorResponse$Stub;-><init>()V
Landroid/accounts/IAccountManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accounts/IAccountManager;
Landroid/accounts/IAccountManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/animation/LayoutTransition;->cancel()V
@@ -236,6 +237,7 @@
Landroid/app/IActivityManager;->getTaskForActivity(Landroid/os/IBinder;Z)I
Landroid/app/IActivityManager;->moveTaskToFront(IILandroid/os/Bundle;)V
Landroid/app/IActivityManager;->publishContentProviders(Landroid/app/IApplicationThread;Ljava/util/List;)V
+Landroid/app/IActivityManager;->requestBugReport(I)V
Landroid/app/IActivityManager;->resumeAppSwitches()V
Landroid/app/IActivityManager;->setRequestedOrientation(Landroid/os/IBinder;I)V
Landroid/app/IActivityManager;->setTaskResizeable(II)V
@@ -609,6 +611,11 @@
Landroid/graphics/Camera;->native_instance:J
Landroid/graphics/Canvas;-><init>(J)V
Landroid/graphics/Canvas;->release()V
+Landroid/graphics/Canvas;->save(I)I
+Landroid/graphics/Canvas;->saveLayer(Landroid/graphics/RectF;Landroid/graphics/Paint;I)I
+Landroid/graphics/Canvas;->saveLayer(FFFFLandroid/graphics/Paint;I)I
+Landroid/graphics/Canvas;->saveLayerAlpha(Landroid/graphics/RectF;II)I
+Landroid/graphics/Canvas;->saveLayerAlpha(FFFFII)I
Landroid/graphics/ColorMatrixColorFilter;->setColorMatrix(Landroid/graphics/ColorMatrix;)V
Landroid/graphics/drawable/AnimatedImageDrawable;->onAnimationEnd()V
Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState;->mStateIds:Landroid/util/SparseIntArray;
@@ -943,6 +950,7 @@
Landroid/media/IAudioService;->setStreamVolume(IIILjava/lang/String;)V
Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService;
Landroid/media/IAudioService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/media/IRemoteDisplayCallback;->onStateChanged(Landroid/media/RemoteDisplayState;)V
Landroid/media/IVolumeController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IVolumeController;
Landroid/media/JetPlayer;->mNativePlayerInJavaObj:J
Landroid/media/JetPlayer;->postEventFromNative(Ljava/lang/Object;III)V
@@ -995,6 +1003,8 @@
Landroid/media/RemoteDisplay;->notifyDisplayConnected(Landroid/view/Surface;IIII)V
Landroid/media/RemoteDisplay;->notifyDisplayDisconnected()V
Landroid/media/RemoteDisplay;->notifyDisplayError(I)V
+Landroid/media/RemoteDisplayState;->displays:Ljava/util/ArrayList;
+Landroid/media/RemoteDisplayState;-><init>()V
Landroid/media/RingtoneManager;->getRingtone(Landroid/content/Context;Landroid/net/Uri;I)Landroid/media/Ringtone;
Landroid/media/session/MediaSessionLegacyHelper;->getHelper(Landroid/content/Context;)Landroid/media/session/MediaSessionLegacyHelper;
Landroid/media/session/MediaSession;->mCallback:Landroid/media/session/MediaSession$CallbackMessageHandler;
@@ -1006,6 +1016,11 @@
Landroid/media/soundtrigger/SoundTriggerManager;->stopRecognition(Ljava/util/UUID;)I
Landroid/media/soundtrigger/SoundTriggerManager;->unloadSoundModel(Ljava/util/UUID;)I
Landroid/media/SubtitleController;->mHandler:Landroid/os/Handler;
+Landroid/media/SubtitleTrack$RenderingWidget;->draw(Landroid/graphics/Canvas;)V
+Landroid/media/SubtitleTrack$RenderingWidget;->onAttachedToWindow()V
+Landroid/media/SubtitleTrack$RenderingWidget;->onDetachedFromWindow()V
+Landroid/media/SubtitleTrack$RenderingWidget;->setOnChangedListener(Landroid/media/SubtitleTrack$RenderingWidget$OnChangedListener;)V
+Landroid/media/SubtitleTrack$RenderingWidget;->setSize(II)V
Landroid/media/ThumbnailUtils;->createImageThumbnail(Ljava/lang/String;I)Landroid/graphics/Bitmap;
Landroid/media/ToneGenerator;->mNativeContext:J
Landroid/media/VolumeShaper$Configuration;-><init>(IIIDI[F[F)V
@@ -1111,6 +1126,8 @@
Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V
Landroid/net/wifi/p2p/WifiP2pManager;->setDeviceName(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Ljava/lang/String;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
+Landroid/net/wifi/p2p/WifiP2pManager$Channel;->mAsyncChannel:Lcom/android/internal/util/AsyncChannel;
+Landroid/net/wifi/p2p/WifiP2pManager$Channel;->putListener(Ljava/lang/Object;)I
Landroid/net/wifi/ScanResult;->anqpDomainId:I
Landroid/net/wifi/ScanResult;->anqpLines:Ljava/util/List;
Landroid/net/wifi/ScanResult;->distanceCm:I
@@ -1961,6 +1978,7 @@
Landroid/util/Rational;->mNumerator:I
Landroid/util/Singleton;->mInstance:Ljava/lang/Object;
Landroid/util/Slog;->d(Ljava/lang/String;Ljava/lang/String;)I
+Landroid/util/Slog;->w(Ljava/lang/String;Ljava/lang/String;)I
Landroid/util/SparseIntArray;->mKeys:[I
Landroid/util/SparseIntArray;->mSize:I
Landroid/util/SparseIntArray;->mValues:[I
@@ -2280,6 +2298,7 @@
Landroid/webkit/IWebViewUpdateService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/webkit/WebResourceResponse;->mImmutable:Z
Landroid/webkit/WebSyncManager;->mHandler:Landroid/os/Handler;
+Landroid/webkit/WebViewClient;->onUnhandledInputEvent(Landroid/webkit/WebView;Landroid/view/InputEvent;)V
Landroid/webkit/WebView;->debugDump()V
Landroid/webkit/WebView;->disablePlatformNotifications()V
Landroid/webkit/WebView;->emulateShiftHeld()V
@@ -2709,11 +2728,15 @@
Lcom/android/internal/R$styleable;->CheckBoxPreference_summaryOn:I
Lcom/android/internal/R$styleable;->CompoundButton_button:I
Lcom/android/internal/R$styleable;->CompoundButton:[I
+Lcom/android/internal/R$styleable;->DialogPreference_dialogTitle:I
+Lcom/android/internal/R$styleable;->DialogPreference:[I
Lcom/android/internal/R$styleable;->EdgeEffect_colorEdgeEffect:I
Lcom/android/internal/R$styleable;->EdgeEffect:[I
Lcom/android/internal/R$styleable;->IconMenuView:[I
Lcom/android/internal/R$styleable;->ImageView:[I
Lcom/android/internal/R$styleable;->ImageView_src:I
+Lcom/android/internal/R$styleable;->ListPreference_entries:I
+Lcom/android/internal/R$styleable;->ListPreference:[I
Lcom/android/internal/R$styleable;->Preference_defaultValue:I
Lcom/android/internal/R$styleable;->Preference_dependency:I
Lcom/android/internal/R$styleable;->Preference_enabled:I
@@ -2779,6 +2802,7 @@
Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I
Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Lcom/android/internal/util/AsyncChannel;->sendMessage(III)V
Lcom/android/internal/util/FastPrintWriter;-><init>(Ljava/io/OutputStream;)V
Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap;
Lcom/android/internal/view/IInputMethodManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodManager;
@@ -2955,6 +2979,7 @@
Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
Ljava/lang/Short;->value:S
Ljava/lang/String;-><init>(II[C)V
+Ljava/lang/System;->arraycopy([II[III)V
Ljava/lang/System;-><init>()V
Ljava/lang/Thread;->daemon:Z
Ljava/lang/Thread;->dispatchUncaughtException(Ljava/lang/Throwable;)V
@@ -3066,5 +3091,7 @@
Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>(Ljavax/net/ssl/SSLSocketFactory;)V
Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>()V
Lorg/json/JSONArray;->values:Ljava/util/List;
+Lorg/json/JSONObject;->append(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
+Lorg/json/JSONObject;->keySet()Ljava/util/Set;
Lorg/json/JSONObject;->writeTo(Lorg/json/JSONStringer;)V
Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 77bb0bb..61ae6e7 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -32,7 +32,6 @@
Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot;
Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
Landroid/app/IActivityManager;->removeTask(I)Z
-Landroid/app/IActivityManager;->requestBugReport(I)V
Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I
Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I
Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I
@@ -108,11 +107,6 @@
Landroid/graphics/Bitmap;->createHardwareBitmap(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;
Landroid/graphics/Canvas;->clipRegion(Landroid/graphics/Region;Landroid/graphics/Region$Op;)Z
Landroid/graphics/Canvas;->clipRegion(Landroid/graphics/Region;)Z
-Landroid/graphics/Canvas;->save(I)I
-Landroid/graphics/Canvas;->saveLayerAlpha(FFFFII)I
-Landroid/graphics/Canvas;->saveLayerAlpha(Landroid/graphics/RectF;II)I
-Landroid/graphics/Canvas;->saveLayer(FFFFLandroid/graphics/Paint;I)I
-Landroid/graphics/Canvas;->saveLayer(Landroid/graphics/RectF;Landroid/graphics/Paint;I)I
Landroid/graphics/drawable/Drawable;->isProjected()Z
Landroid/graphics/drawable/Drawable;->updateTintFilter(Landroid/graphics/PorterDuffColorFilter;Landroid/content/res/ColorStateList;Landroid/graphics/PorterDuff$Mode;)Landroid/graphics/PorterDuffColorFilter;
Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d3c1e99..4326ee3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1219,11 +1219,11 @@
public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
/**
- * This is set on the notification shown by the activity manager about all apps
- * running in the background. It indicates that the notification should be shown
- * only if any of the given apps do not already have a {@link #FLAG_FOREGROUND_SERVICE}
- * notification currently visible to the user. This is a string array of all
- * package names of the apps.
+ * This is set on the notifications shown by system_server about apps running foreground
+ * services. It indicates that the notification should be shown
+ * only if any of the given apps do not already have a properly tagged
+ * {@link #FLAG_FOREGROUND_SERVICE} notification currently visible to the user.
+ * This is a string array of all package names of the apps.
* @hide
*/
public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 8f01685..bd4933a 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -24,7 +24,6 @@
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -47,6 +46,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
+
import libcore.io.IoUtils;
import java.io.IOException;
@@ -876,15 +876,35 @@
}
/**
+ * Grants a runtime permission to a package.
+ * @param packageName The package to which to grant.
+ * @param permission The permission to grant.
+ * @throws SecurityException if unable to grant the permission.
+ */
+ public void grantRuntimePermission(String packageName, String permission) {
+ grantRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
+ }
+
+ /**
+ * @deprecated replaced by
+ * {@link #grantRuntimePermissionAsUser(String, String, UserHandle)}.
+ * @hide
+ */
+ @Deprecated
+ @TestApi
+ public boolean grantRuntimePermission(String packageName, String permission,
+ UserHandle userHandle) {
+ grantRuntimePermissionAsUser(packageName, permission, userHandle);
+ return true;
+ }
+
+ /**
* Grants a runtime permission to a package for a user.
* @param packageName The package to which to grant.
* @param permission The permission to grant.
- * @return Whether granting succeeded.
- *
- * @hide
+ * @throws SecurityException if unable to grant the permission.
*/
- @TestApi
- public boolean grantRuntimePermission(String packageName, String permission,
+ public void grantRuntimePermissionAsUser(String packageName, String permission,
UserHandle userHandle) {
synchronized (mLock) {
throwIfNotConnectedLocked();
@@ -896,25 +916,42 @@
// Calling out without a lock held.
mUiAutomationConnection.grantRuntimePermission(packageName,
permission, userHandle.getIdentifier());
- // TODO: The package manager API should return boolean.
- return true;
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error granting runtime permission", re);
+ } catch (Exception e) {
+ throw new SecurityException("Error granting runtime permission", e);
}
- return false;
}
/**
- * Revokes a runtime permission from a package for a user.
- * @param packageName The package from which to revoke.
- * @param permission The permission to revoke.
- * @return Whether revoking succeeded.
- *
+ * Revokes a runtime permission from a package.
+ * @param packageName The package to which to grant.
+ * @param permission The permission to grant.
+ * @throws SecurityException if unable to revoke the permission.
+ */
+ public void revokeRuntimePermission(String packageName, String permission) {
+ revokeRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
+ }
+
+ /**
+ * @deprecated replaced by
+ * {@link #revokeRuntimePermissionAsUser(String, String, UserHandle)}.
* @hide
*/
+ @Deprecated
@TestApi
public boolean revokeRuntimePermission(String packageName, String permission,
UserHandle userHandle) {
+ revokeRuntimePermissionAsUser(packageName, permission, userHandle);
+ return true;
+ }
+
+ /**
+ * Revokes a runtime permission from a package.
+ * @param packageName The package to which to grant.
+ * @param permission The permission to grant.
+ * @throws SecurityException if unable to revoke the permission.
+ */
+ public void revokeRuntimePermissionAsUser(String packageName, String permission,
+ UserHandle userHandle) {
synchronized (mLock) {
throwIfNotConnectedLocked();
}
@@ -925,12 +962,9 @@
// Calling out without a lock held.
mUiAutomationConnection.revokeRuntimePermission(packageName,
permission, userHandle.getIdentifier());
- // TODO: The package manager API should return boolean.
- return true;
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error revoking runtime permission", re);
+ } catch (Exception e) {
+ throw new SecurityException("Error granting runtime permission", e);
}
- return false;
}
/**
@@ -949,6 +983,7 @@
synchronized (mLock) {
throwIfNotConnectedLocked();
}
+ warnIfBetterCommand(command);
ParcelFileDescriptor source = null;
ParcelFileDescriptor sink = null;
@@ -991,6 +1026,7 @@
synchronized (mLock) {
throwIfNotConnectedLocked();
}
+ warnIfBetterCommand(command);
ParcelFileDescriptor source_read = null;
ParcelFileDescriptor sink_read = null;
@@ -1056,6 +1092,16 @@
}
}
+ private void warnIfBetterCommand(String cmd) {
+ if (cmd.startsWith("pm grant ")) {
+ Log.w(LOG_TAG, "UiAutomation.grantRuntimePermission() "
+ + "is more robust and should be used instead of 'pm grant'");
+ } else if (cmd.startsWith("pm revoke ")) {
+ Log.w(LOG_TAG, "UiAutomation.revokeRuntimePermission() "
+ + "is more robust and should be used instead of 'pm revoke'");
+ }
+ }
+
private class IAccessibilityServiceClientImpl extends IAccessibilityServiceClientWrapper {
public IAccessibilityServiceClientImpl(Looper looper) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index aec33b3..1534a15 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -124,6 +124,7 @@
@SystemService(Context.DEVICE_POLICY_SERVICE)
@RequiresFeature(PackageManager.FEATURE_DEVICE_ADMIN)
public class DevicePolicyManager {
+
private static String TAG = "DevicePolicyManager";
private final Context mContext;
@@ -1612,8 +1613,6 @@
* <li>keyguard
* </ul>
*
- * This is the default configuration for LockTask.
- *
* @see #setLockTaskFeatures(ComponentName, int)
*/
public static final int LOCK_TASK_FEATURE_NONE = 0;
@@ -1631,7 +1630,10 @@
/**
* Enable notifications during LockTask mode. This includes notification icons on the status
* bar, heads-up notifications, and the expandable notification shade. Note that the Quick
- * Settings panel will still be disabled.
+ * Settings panel remains disabled. This feature flag can only be used in combination with
+ * {@link #LOCK_TASK_FEATURE_HOME}. {@link #setLockTaskFeatures(ComponentName, int)}
+ * throws an {@link IllegalArgumentException} if this feature flag is defined without
+ * {@link #LOCK_TASK_FEATURE_HOME}.
*
* @see #setLockTaskFeatures(ComponentName, int)
*/
@@ -1664,6 +1666,9 @@
* the user long-presses the power button, for example. Note that the user may not be able to
* power off the device if this flag is not set.
*
+ * <p>This flag is enabled by default until {@link #setLockTaskFeatures(ComponentName, int)} is
+ * called for the first time.
+ *
* @see #setLockTaskFeatures(ComponentName, int)
*/
public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 1 << 4;
@@ -1747,6 +1752,25 @@
public static final int ID_TYPE_MEID = 8;
/**
+ * Specifies that the calling app should be granted access to the installed credentials
+ * immediately. Otherwise, access to the credentials will be gated by user approval.
+ * For use with {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)}
+ *
+ * @see #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)
+ */
+ public static final int INSTALLKEY_REQUEST_CREDENTIALS_ACCESS = 1;
+
+ /**
+ * Specifies that a user can select the key via the Certificate Selection prompt.
+ * If this flag is not set when calling {@link #installKeyPair}, the key can only be granted
+ * access by implementing {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias}.
+ * For use with {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)}
+ *
+ * @see #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)
+ */
+ public static final int INSTALLKEY_SET_USER_SELECTABLE = 2;
+
+ /**
* Broadcast action: sent when the profile owner is set, changed or cleared.
*
* This broadcast is sent only to the user managed by the new profile owner.
@@ -4122,7 +4146,11 @@
*/
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
@NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess) {
- return installKeyPair(admin, privKey, certs, alias, requestAccess, true);
+ int flags = INSTALLKEY_SET_USER_SELECTABLE;
+ if (requestAccess) {
+ flags |= INSTALLKEY_REQUEST_CREDENTIALS_ACCESS;
+ }
+ return installKeyPair(admin, privKey, certs, alias, flags);
}
/**
@@ -4146,13 +4174,9 @@
* {@link android.security.KeyChain#getCertificateChain}.
* @param alias The private key alias under which to install the certificate. If a certificate
* with that alias already exists, it will be overwritten.
- * @param requestAccess {@code true} to request that the calling app be granted access to the
- * credentials immediately. Otherwise, access to the credentials will be gated by user
- * approval.
- * @param isUserSelectable {@code true} to indicate that a user can select this key via the
- * Certificate Selection prompt, false to indicate that this key can only be granted
- * access by implementing
- * {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias}.
+ * @param flags Flags to request that the calling app be granted access to the credentials
+ * and set the key to be user-selectable. See {@link #INSTALLKEY_SET_USER_SELECTABLE} and
+ * {@link #INSTALLKEY_REQUEST_CREDENTIALS_ACCESS}.
* @return {@code true} if the keys were installed, {@code false} otherwise.
* @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
* owner.
@@ -4161,9 +4185,12 @@
* @see #DELEGATION_CERT_INSTALL
*/
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
- @NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess,
- boolean isUserSelectable) {
+ @NonNull Certificate[] certs, @NonNull String alias, int flags) {
throwIfParentInstance("installKeyPair");
+ boolean requestAccess = (flags & INSTALLKEY_REQUEST_CREDENTIALS_ACCESS)
+ == INSTALLKEY_REQUEST_CREDENTIALS_ACCESS;
+ boolean isUserSelectable = (flags & INSTALLKEY_SET_USER_SELECTABLE)
+ == INSTALLKEY_SET_USER_SELECTABLE;
try {
final byte[] pemCert = Credentials.convertToPem(certs[0]);
byte[] pemChain = null;
@@ -4242,6 +4269,8 @@
* algorithm specification in {@code keySpec} is not {@code RSAKeyGenParameterSpec}
* or {@code ECGenParameterSpec}, or if Device ID attestation was requested but the
* {@code keySpec} does not contain an attestation challenge.
+ * @throws UnsupportedOperationException if Device ID attestation was requested but the
+ * underlying hardware does not support it.
* @see KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])
*/
public AttestedKeyPair generateKeyPair(@Nullable ComponentName admin,
@@ -6359,6 +6388,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public @Nullable List<String> getPermittedInputMethodsForCurrentUser() {
throwIfParentInstance("getPermittedInputMethodsForCurrentUser");
if (mService != null) {
@@ -7186,30 +7216,24 @@
}
/**
- * Sets which system features to enable for LockTask mode.
- * <p>
- * Feature flags set through this method will only take effect for the duration when the device
- * is in LockTask mode. If this method is not called, none of the features listed here will be
- * enabled.
- * <p>
- * This function can only be called by the device owner, a profile owner of an affiliated user
- * or profile, or the profile owner when no device owner is set. See {@link #isAffiliatedUser}.
- * Any features set via this method will be cleared if the user becomes unaffiliated.
+ * Sets which system features are enabled when the device runs in lock task mode. This method
+ * doesn't affect the features when lock task mode is inactive. Any system features not included
+ * in {@code flags} are implicitly disabled when calling this method. By default, only
+ * {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS} is enabled—all the other features are disabled. To
+ * disable the global actions dialog, call this method omitting
+ * {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS}.
+ *
+ * <p>This method can only be called by the device owner, a profile owner of an affiliated
+ * user or profile, or the profile owner when no device owner is set. See
+ * {@link #isAffiliatedUser}.
+ * Any features set using this method are cleared if the user becomes unaffiliated.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param flags Bitfield of feature flags:
- * {@link #LOCK_TASK_FEATURE_NONE} (default),
- * {@link #LOCK_TASK_FEATURE_SYSTEM_INFO},
- * {@link #LOCK_TASK_FEATURE_NOTIFICATIONS},
- * {@link #LOCK_TASK_FEATURE_HOME},
- * {@link #LOCK_TASK_FEATURE_OVERVIEW},
- * {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS},
- * {@link #LOCK_TASK_FEATURE_KEYGUARD}
+ * @param flags The system features enabled during lock task mode.
* @throws SecurityException if {@code admin} is not the device owner, the profile owner of an
* affiliated user or profile, or the profile owner when no device owner is set.
* @see #isAffiliatedUser
- * @throws SecurityException if {@code admin} is not the device owner or the profile owner.
- */
+ **/
public void setLockTaskFeatures(@NonNull ComponentName admin, @LockTaskFeature int flags) {
throwIfParentInstance("setLockTaskFeatures");
if (mService != null) {
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 6feb527..d272652 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -179,6 +179,13 @@
public static final int REASON_SUB_USAGE_ACTIVE_TIMEOUT = 0x0007;
/** @hide */
public static final int REASON_SUB_USAGE_SYNC_ADAPTER = 0x0008;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_SLICE_PINNED = 0x0009;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_SLICE_PINNED_PRIV = 0x000A;
+
+ /** @hide */
+ public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001;
/** @hide */
@IntDef(flag = false, prefix = { "STANDBY_BUCKET_" }, value = {
@@ -616,36 +623,47 @@
break;
case REASON_MAIN_PREDICTED:
sb.append("p");
+ switch (standbyReason & REASON_SUB_MASK) {
+ case REASON_SUB_PREDICTED_RESTORED:
+ sb.append("-r");
+ break;
+ }
break;
case REASON_MAIN_TIMEOUT:
sb.append("t");
break;
case REASON_MAIN_USAGE:
- sb.append("u-");
+ sb.append("u");
switch (standbyReason & REASON_SUB_MASK) {
case REASON_SUB_USAGE_SYSTEM_INTERACTION:
- sb.append("si");
+ sb.append("-si");
break;
case REASON_SUB_USAGE_NOTIFICATION_SEEN:
- sb.append("ns");
+ sb.append("-ns");
break;
case REASON_SUB_USAGE_USER_INTERACTION:
- sb.append("ui");
+ sb.append("-ui");
break;
case REASON_SUB_USAGE_MOVE_TO_FOREGROUND:
- sb.append("mf");
+ sb.append("-mf");
break;
case REASON_SUB_USAGE_MOVE_TO_BACKGROUND:
- sb.append("mb");
+ sb.append("-mb");
break;
case REASON_SUB_USAGE_SYSTEM_UPDATE:
- sb.append("su");
+ sb.append("-su");
break;
case REASON_SUB_USAGE_ACTIVE_TIMEOUT:
- sb.append("at");
+ sb.append("-at");
break;
case REASON_SUB_USAGE_SYNC_ADAPTER:
- sb.append("sa");
+ sb.append("-sa");
+ break;
+ case REASON_SUB_USAGE_SLICE_PINNED:
+ sb.append("slp");
+ break;
+ case REASON_SUB_USAGE_SLICE_PINNED_PRIV:
+ sb.append("slpp");
break;
}
break;
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index cb1d106..af99bf7 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -701,29 +701,6 @@
}
/**
- * Sends Virtual Cable Unplug to currently connected host.
- *
- * @return
- * {@hide}
- */
- public boolean unplug(BluetoothDevice device) {
- boolean result = false;
-
- final IBluetoothHidDevice service = mService;
- if (service != null) {
- try {
- result = service.unplug(device);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- }
-
- return result;
- }
-
- /**
* Initiates connection to host which is currently paired with this device. If the application
* is not registered, #connect(BluetoothDevice) will fail. The connection state should be
* tracked by the application by handling callback from Callback#onConnectionStateChanged. The
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index a3b2d22..efb9517 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -535,6 +535,19 @@
}
/**
+ * Set the level of color saturation to apply to the display.
+ * @param level The amount of saturation to apply, between 0 and 1 inclusive.
+ * 0 produces a grayscale image, 1 is normal.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
+ public void setSaturationLevel(float level) {
+ mGlobal.setSaturationLevel(level);
+ }
+
+ /**
* Creates a virtual display.
*
* @see #createVirtualDisplay(String, int, int, int, Surface, int,
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 1f67a6b..2d0ef2f 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -384,6 +384,17 @@
}
}
+ /**
+ * Set the level of color saturation to apply to the display.
+ */
+ public void setSaturationLevel(float level) {
+ try {
+ mDm.setSaturationLevel(level);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
String name, int width, int height, int densityDpi, Surface surface, int flags,
VirtualDisplay.Callback callback, Handler handler, String uniqueId) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 9fcb9d3..b77de748 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -65,6 +65,9 @@
// Requires CONFIGURE_DISPLAY_COLOR_MODE
void requestColorMode(int displayId, int colorMode);
+ // Requires CONTROL_DISPLAY_SATURATION
+ void setSaturationLevel(float level);
+
// Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate
// MediaProjection token for certain combinations of flags.
int createVirtualDisplay(in IVirtualDisplayCallback callback,
diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java
index b2aa9ba..e6f523c 100644
--- a/core/java/android/hardware/radio/ProgramList.java
+++ b/core/java/android/hardware/radio/ProgramList.java
@@ -263,6 +263,17 @@
/**
* @hide for framework use only
*/
+ public Filter() {
+ mIdentifierTypes = Collections.emptySet();
+ mIdentifiers = Collections.emptySet();
+ mIncludeCategories = false;
+ mExcludeModifications = false;
+ mVendorFilter = null;
+ }
+
+ /**
+ * @hide for framework use only
+ */
public Filter(@Nullable Map<String, String> vendorFilter) {
mIdentifierTypes = Collections.emptySet();
mIdentifiers = Collections.emptySet();
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 8fde82e..8263bb8 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -211,6 +211,7 @@
private final String mSerial;
private final int mNumTuners;
private final int mNumAudioSources;
+ private final boolean mIsInitializationRequired;
private final boolean mIsCaptureSupported;
private final BandDescriptor[] mBands;
private final boolean mIsBgScanSupported;
@@ -222,7 +223,8 @@
/** @hide */
public ModuleProperties(int id, String serviceName, int classId, String implementor,
String product, String version, String serial, int numTuners, int numAudioSources,
- boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
+ boolean isInitializationRequired, boolean isCaptureSupported,
+ BandDescriptor[] bands, boolean isBgScanSupported,
@ProgramSelector.ProgramType int[] supportedProgramTypes,
@ProgramSelector.IdentifierType int[] supportedIdentifierTypes,
@Nullable Map<String, Integer> dabFrequencyTable,
@@ -236,6 +238,7 @@
mSerial = serial;
mNumTuners = numTuners;
mNumAudioSources = numAudioSources;
+ mIsInitializationRequired = isInitializationRequired;
mIsCaptureSupported = isCaptureSupported;
mBands = bands;
mIsBgScanSupported = isBgScanSupported;
@@ -329,6 +332,18 @@
return mNumAudioSources;
}
+ /**
+ * Checks, if BandConfig initialization (after {@link RadioManager#openTuner})
+ * is required to be done before other operations or not.
+ *
+ * If it is, the client has to wait for {@link RadioTuner.Callback#onConfigurationChanged}
+ * callback before executing any other operations. Otherwise, such operation will fail
+ * returning {@link RadioManager#STATUS_INVALID_OPERATION} error code.
+ */
+ public boolean isInitializationRequired() {
+ return mIsInitializationRequired;
+ }
+
/** {@code true} if audio capture is possible from radio tuner output.
* This indicates if routing to audio devices not connected to the same HAL as the FM radio
* is possible (e.g. to USB) or DAR (Digital Audio Recorder) feature can be implemented.
@@ -419,6 +434,7 @@
mSerial = in.readString();
mNumTuners = in.readInt();
mNumAudioSources = in.readInt();
+ mIsInitializationRequired = in.readInt() == 1;
mIsCaptureSupported = in.readInt() == 1;
Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader());
mBands = new BandDescriptor[tmp.length];
@@ -454,6 +470,7 @@
dest.writeString(mSerial);
dest.writeInt(mNumTuners);
dest.writeInt(mNumAudioSources);
+ dest.writeInt(mIsInitializationRequired ? 1 : 0);
dest.writeInt(mIsCaptureSupported ? 1 : 0);
dest.writeParcelableArray(mBands, flags);
dest.writeInt(mIsBgScanSupported ? 1 : 0);
@@ -476,6 +493,7 @@
+ ", mVersion=" + mVersion + ", mSerial=" + mSerial
+ ", mNumTuners=" + mNumTuners
+ ", mNumAudioSources=" + mNumAudioSources
+ + ", mIsInitializationRequired=" + mIsInitializationRequired
+ ", mIsCaptureSupported=" + mIsCaptureSupported
+ ", mIsBgScanSupported=" + mIsBgScanSupported
+ ", mBands=" + Arrays.toString(mBands) + "]";
@@ -484,8 +502,8 @@
@Override
public int hashCode() {
return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion,
- mSerial, mNumTuners, mNumAudioSources, mIsCaptureSupported, mBands,
- mIsBgScanSupported, mDabFrequencyTable, mVendorInfo);
+ mSerial, mNumTuners, mNumAudioSources, mIsInitializationRequired,
+ mIsCaptureSupported, mBands, mIsBgScanSupported, mDabFrequencyTable, mVendorInfo);
}
@Override
@@ -503,6 +521,7 @@
if (!Objects.equals(mSerial, other.mSerial)) return false;
if (mNumTuners != other.mNumTuners) return false;
if (mNumAudioSources != other.mNumAudioSources) return false;
+ if (mIsInitializationRequired != other.mIsInitializationRequired) return false;
if (mIsCaptureSupported != other.mIsCaptureSupported) return false;
if (!Objects.equals(mBands, other.mBands)) return false;
if (mIsBgScanSupported != other.mIsBgScanSupported) return false;
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index 85f3115..be2846f 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -60,6 +60,7 @@
mLegacyListProxy.close();
mLegacyListProxy = null;
}
+ mCallback.close();
}
try {
mTuner.close();
@@ -278,6 +279,7 @@
try {
mTuner.startProgramListUpdates(filter);
} catch (UnsupportedOperationException ex) {
+ Log.i(TAG, "Program list is not supported with this hardware");
return null;
} catch (RemoteException ex) {
mCallback.setProgramListObserver(null, () -> { });
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index 7437c40..0fb93e5 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -53,6 +53,12 @@
}
}
+ void close() {
+ synchronized (mLock) {
+ if (mProgramList != null) mProgramList.close();
+ }
+ }
+
void setProgramListObserver(@Nullable ProgramList programList,
@NonNull ProgramList.OnCloseListener closeListener) {
Objects.requireNonNull(closeListener);
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index 402c995..116262e 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -47,10 +47,10 @@
* Uses AlarmManager.setRepeating API, so if the timestamp is in past, alarm fires immediately,
* and alarm is inexact.
*/
- oneway void setPullingAlarms(long timestampMs, long intervalMs);
+ oneway void setPullingAlarm(long nextPullTimeMs);
/** Cancel any repeating pulling alarm. */
- oneway void cancelPullingAlarms();
+ oneway void cancelPullingAlarm();
/**
* Register an alarm when we want to trigger subscribers at the given
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 5be72bc..094f004 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -158,7 +158,7 @@
* @hide
*/
public static boolean isCore(int uid) {
- if (uid > 0) {
+ if (uid >= 0) {
final int appId = getAppId(uid);
return appId < Process.FIRST_APPLICATION_UID;
} else {
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index ca4c796..b9dd376 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -32,6 +32,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.UUID;
@@ -158,6 +159,13 @@
private final Object mLock = new Object();
/**
+ * List of exemptions to the API blacklist. These are prefix matches on the runtime format
+ * symbol signature. Any matching symbol is treated by the runtime as being on the light grey
+ * list.
+ */
+ private List<String> mApiBlacklistExemptions = Collections.emptyList();
+
+ /**
* The state of the connection to the primary zygote.
*/
private ZygoteState primaryZygoteState;
@@ -175,7 +183,7 @@
* The process will continue running after this function returns.
*
* <p>If processes are not enabled, a new thread in the caller's
- * process is created and main() of <var>processClass</var> called there.
+ * process is created and main() of <var>processclass</var> called there.
*
* <p>The niceName parameter, if not an empty string, is a custom name to
* give to the process instead of using processClass. This allows you to
@@ -454,6 +462,49 @@
}
/**
+ * Push hidden API blacklisting exemptions into the zygote process(es).
+ *
+ * <p>The list of exemptions will take affect for all new processes forked from the zygote after
+ * this call.
+ *
+ * @param exemptions List of hidden API exemption prefixes.
+ */
+ public void setApiBlacklistExemptions(List<String> exemptions) {
+ synchronized (mLock) {
+ mApiBlacklistExemptions = exemptions;
+ maybeSetApiBlacklistExemptions(primaryZygoteState, true);
+ maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
+ if (state == null || state.isClosed()) {
+ return;
+ }
+ if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) {
+ return;
+ }
+ try {
+ state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
+ state.writer.newLine();
+ state.writer.write("--set-api-blacklist-exemptions");
+ state.writer.newLine();
+ for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) {
+ state.writer.write(mApiBlacklistExemptions.get(i));
+ state.writer.newLine();
+ }
+ state.writer.flush();
+ int status = state.inputStream.readInt();
+ if (status != 0) {
+ Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status);
+ }
+ } catch (IOException ioe) {
+ Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe);
+ }
+ }
+
+ /**
* Tries to open socket to Zygote process if not already open. If
* already open, does nothing. May block and retry. Requires that mLock be held.
*/
@@ -467,8 +518,8 @@
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
+ maybeSetApiBlacklistExemptions(primaryZygoteState, false);
}
-
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
}
@@ -480,6 +531,7 @@
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
+ maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
}
if (secondaryZygoteState.matches(abi)) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 70f343e..b7f6cde 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7809,6 +7809,14 @@
"suppress_auto_battery_saver_suggestion";
/**
+ * List of packages, which data need to be unconditionally cleared before full restore.
+ * Type: string
+ * @hide
+ */
+ public static final String PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE =
+ "packages_to_clear_data_before_full_restore";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
diff --git a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
index 2a66206..4af1af5 100644
--- a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
+++ b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
@@ -50,6 +50,22 @@
*/
@SystemApi
public final class KeyChainProtectionParams implements Parcelable {
+
+ // IMPORTANT! PLEASE READ!
+ // -----------------------
+ // If you edit this file (e.g., to add new fields), please MAKE SURE to also do the following:
+ // - Update the #writeToParcel(Parcel) method below
+ // - Update the #(Parcel) constructor below
+ // - Update android.security.keystore.recovery.KeyChainSnapshotTest to make sure nobody
+ // accidentally breaks your fields in the Parcel in the future.
+ // - Update com.android.server.locksettings.recoverablekeystore.serialization
+ // .KeyChainSnapshotSerializer to correctly serialize your new field
+ // - Update com.android.server.locksettings.recoverablekeystore.serialization
+ // .KeyChainSnapshotSerializer to correctly deserialize your new field
+ // - Update com.android.server.locksettings.recoverablekeystore.serialization
+ // .KeychainSnapshotSerializerTest to make sure nobody breaks serialization of your field
+ // in the future.
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"TYPE_"}, value = {TYPE_LOCKSCREEN})
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
index 24ff182..e46c34c 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -48,6 +48,22 @@
*/
@SystemApi
public final class KeyChainSnapshot implements Parcelable {
+
+ // IMPORTANT! PLEASE READ!
+ // -----------------------
+ // If you edit this file (e.g., to add new fields), please MAKE SURE to also do the following:
+ // - Update the #writeToParcel(Parcel) method below
+ // - Update the #(Parcel) constructor below
+ // - Update android.security.keystore.recovery.KeyChainSnapshotTest to make sure nobody
+ // accidentally breaks your fields in the Parcel in the future.
+ // - Update com.android.server.locksettings.recoverablekeystore.serialization
+ // .KeyChainSnapshotSerializer to correctly serialize your new field
+ // - Update com.android.server.locksettings.recoverablekeystore.serialization
+ // .KeyChainSnapshotSerializer to correctly deserialize your new field
+ // - Update com.android.server.locksettings.recoverablekeystore.serialization
+ // .KeychainSnapshotSerializerTest to make sure nobody breaks serialization of your field
+ // in the future.
+
private static final int DEFAULT_MAX_ATTEMPTS = 10;
private static final long DEFAULT_COUNTER_ID = 1L;
diff --git a/core/java/android/security/keystore/recovery/KeyDerivationParams.java b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
index 225b592..d16f3ea 100644
--- a/core/java/android/security/keystore/recovery/KeyDerivationParams.java
+++ b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
@@ -35,6 +35,22 @@
*/
@SystemApi
public final class KeyDerivationParams implements Parcelable {
+
+ // IMPORTANT! PLEASE READ!
+ // -----------------------
+ // If you edit this file (e.g., to add new fields), please MAKE SURE to also do the following:
+ // - Update the #writeToParcel(Parcel) method below
+ // - Update the #(Parcel) constructor below
+ // - Update android.security.keystore.recovery.KeyChainSnapshotTest to make sure nobody
+ // accidentally breaks your fields in the Parcel in the future.
+ // - Update com.android.server.locksettings.recoverablekeystore.serialization
+ // .KeyChainSnapshotSerializer to correctly serialize your new field
+ // - Update com.android.server.locksettings.recoverablekeystore.serialization
+ // .KeyChainSnapshotSerializer to correctly deserialize your new field
+ // - Update com.android.server.locksettings.recoverablekeystore.serialization
+ // .KeychainSnapshotSerializerTest to make sure nobody breaks serialization of your field
+ // in the future.
+
private final int mAlgorithm;
private final byte[] mSalt;
private final int mMemoryDifficulty;
diff --git a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
index 714e35a..32952db 100644
--- a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
+++ b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
@@ -42,6 +42,21 @@
// The only supported format is AES-256 symmetric key.
private byte[] mEncryptedKeyMaterial;
+ // IMPORTANT! PLEASE READ!
+ // -----------------------
+ // If you edit this file (e.g., to add new fields), please MAKE SURE to also do the following:
+ // - Update the #writeToParcel(Parcel) method below
+ // - Update the #(Parcel) constructor below
+ // - Update android.security.keystore.recovery.KeyChainSnapshotTest to make sure nobody
+ // accidentally breaks your fields in the Parcel in the future.
+ // - Update com.android.server.locksettings.recoverablekeystore.serialization
+ // .KeyChainSnapshotSerializer to correctly serialize your new field
+ // - Update com.android.server.locksettings.recoverablekeystore.serialization
+ // .KeyChainSnapshotSerializer to correctly deserialize your new field
+ // - Update com.android.server.locksettings.recoverablekeystore.serialization
+ // .KeychainSnapshotSerializerTest to make sure nobody breaks serialization of your field
+ // in the future.
+
/**
* Builder for creating {@link WrappedApplicationKey}.
*/
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index cf16749..99d45f4 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -113,23 +113,67 @@
/**
* Calculates field classification scores in a batch.
*
- * <p>See {@link AutofillFieldClassificationService} for more info about field classification
- * scores.
+ * <p>A field classification score is a {@code float} representing how well an
+ * {@link AutofillValue} filled matches a expected value predicted by an autofill service
+ * —a full-match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}.
*
- * @param algorithm name of the algorithm to be used to calculate the scores. If invalid, the
- * default algorithm will be used instead.
- * @param args optional arguments to be passed to the algorithm.
+ * <p>The exact score depends on the algorithm used to calculate it— the service must
+ * provide at least one default algorithm (which is used when the algorithm is not specified
+ * or is invalid), but it could provide more (in which case the algorithm name should be
+ * specifiied by the caller when calculating the scores).
+ *
+ * <p>For example, if the service provides an algorithm named {@code EXACT_MATCH} that
+ * returns {@code 1.0} if all characters in match or {@code 0.0} otherwise, a call to:
+ *
+ * <pre>
+ * service.onGetScores("EXACT_MATCH", null,
+ * Arrays.asList(AutofillValue.forText("email1"), AutofillValue.forText("PHONE1")),
+ * Arrays.asList("email1", "phone1"));
+ * </pre>
+ *
+ * <p>Returns:
+ *
+ * <pre>
+ * [
+ * [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+ * [0.0, 0.0] // "PHONE1" compared against ["email1", "phone1"]
+ * ];
+ * </pre>
+ *
+ * <p>If the same algorithm allows the caller to specify whether the comparisons should be
+ * case sensitive by passing a boolean option named {@code "case_sensitive"}, then a call to:
+ *
+ * <pre>
+ * Bundle algorithmOptions = new Bundle();
+ * algorithmOptions.putBoolean("case_sensitive", false);
+ *
+ * service.onGetScores("EXACT_MATCH", algorithmOptions,
+ * Arrays.asList(AutofillValue.forText("email1"), AutofillValue.forText("PHONE1")),
+ * Arrays.asList("email1", "phone1"));
+ * </pre>
+ *
+ * <p>Returns:
+ *
+ * <pre>
+ * [
+ * [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+ * [0.0, 1.0] // "PHONE1" compared against ["email1", "phone1"]
+ * ];
+ * </pre>
+ *
+ * @param algorithm name of the algorithm to be used to calculate the scores. If invalid or
+ * {@code null}, the default algorithm is used instead.
+ * @param algorithmOptions optional arguments to be passed to the algorithm.
* @param actualValues values entered by the user.
* @param userDataValues values predicted from the user data.
- * @return the calculated scores, with the first dimension representing actual values and the
- * second dimension values from {@link UserData}.
+ * @return the calculated scores of {@code actualValues} x {@code userDataValues}.
*
* {@hide}
*/
@Nullable
@SystemApi
public float[][] onGetScores(@Nullable String algorithm,
- @Nullable Bundle args, @NonNull List<AutofillValue> actualValues,
+ @Nullable Bundle algorithmOptions, @NonNull List<AutofillValue> actualValues,
@NonNull List<String> userDataValues) {
Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScore()");
return null;
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 630007b..b413d48 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -43,8 +43,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.ZonedDateTime;
import java.util.ArrayList;
-import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -512,7 +512,7 @@
public static final class Options implements Parcelable {
private @Nullable LocaleList mDefaultLocales;
- private @Nullable Calendar mReferenceTime;
+ private @Nullable ZonedDateTime mReferenceTime;
public Options() {}
@@ -531,7 +531,7 @@
* be interpreted. This should usually be the time when the text was originally
* composed. If no reference time is set, now is used.
*/
- public Options setReferenceTime(Calendar referenceTime) {
+ public Options setReferenceTime(ZonedDateTime referenceTime) {
mReferenceTime = referenceTime;
return this;
}
@@ -550,7 +550,7 @@
* interpreted.
*/
@Nullable
- public Calendar getReferenceTime() {
+ public ZonedDateTime getReferenceTime() {
return mReferenceTime;
}
@@ -567,7 +567,7 @@
}
dest.writeInt(mReferenceTime != null ? 1 : 0);
if (mReferenceTime != null) {
- dest.writeSerializable(mReferenceTime);
+ dest.writeString(mReferenceTime.toString());
}
}
@@ -589,7 +589,7 @@
mDefaultLocales = LocaleList.CREATOR.createFromParcel(in);
}
if (in.readInt() > 0) {
- mReferenceTime = (Calendar) in.readSerializable();
+ mReferenceTime = ZonedDateTime.parse(in.readString());
}
}
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 5ba470a..8d1ed0e 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -16,6 +16,8 @@
package android.view.textclassifier;
+import static java.time.temporal.ChronoUnit.MILLIS;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
@@ -45,9 +47,10 @@
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
+import java.time.Instant;
+import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -119,7 +122,7 @@
&& rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
final String localesString = concatenateLocales(locales);
- final Calendar refTime = Calendar.getInstance();
+ final ZonedDateTime refTime = ZonedDateTime.now();
final boolean darkLaunchAllowed = options != null && options.isDarkLaunchAllowed();
final TextClassifierImplNative nativeImpl = getNative(locales);
final String string = text.toString();
@@ -143,8 +146,8 @@
nativeImpl.classifyText(
string, start, end,
new TextClassifierImplNative.ClassificationOptions(
- refTime.getTimeInMillis(),
- refTime.getTimeZone().getID(),
+ refTime.toInstant().toEpochMilli(),
+ refTime.getZone().getId(),
localesString));
final int size = results.length;
for (int i = 0; i < size; i++) {
@@ -183,19 +186,20 @@
final String string = text.toString();
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
final String localesString = concatenateLocales(locales);
- final Calendar refTime = (options != null && options.getReferenceTime() != null)
- ? options.getReferenceTime() : Calendar.getInstance();
+ final ZonedDateTime refTime =
+ (options != null && options.getReferenceTime() != null)
+ ? options.getReferenceTime() : ZonedDateTime.now();
final TextClassifierImplNative.ClassificationResult[] results =
getNative(locales)
.classifyText(string, startIndex, endIndex,
new TextClassifierImplNative.ClassificationOptions(
- refTime.getTimeInMillis(),
- refTime.getTimeZone().getID(),
+ refTime.toInstant().toEpochMilli(),
+ refTime.getZone().getId(),
localesString));
if (results.length > 0) {
return createClassificationResult(
- results, string, startIndex, endIndex, refTime);
+ results, string, startIndex, endIndex, refTime.toInstant());
}
}
} catch (Throwable t) {
@@ -224,7 +228,7 @@
try {
final long startTimeMs = System.currentTimeMillis();
final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null;
- final Calendar refTime = Calendar.getInstance();
+ final ZonedDateTime refTime = ZonedDateTime.now();
final Collection<String> entitiesToIdentify =
options != null && options.getEntityConfig() != null
? options.getEntityConfig().resolveEntityListModifications(
@@ -236,8 +240,8 @@
nativeImpl.annotate(
textString,
new TextClassifierImplNative.AnnotationOptions(
- refTime.getTimeInMillis(),
- refTime.getTimeZone().getID(),
+ refTime.toInstant().toEpochMilli(),
+ refTime.getZone().getId(),
concatenateLocales(defaultLocales)));
for (TextClassifierImplNative.AnnotatedSpan span : annotations) {
final TextClassifierImplNative.ClassificationResult[] results =
@@ -416,7 +420,7 @@
private TextClassification createClassificationResult(
TextClassifierImplNative.ClassificationResult[] classifications,
- String text, int start, int end, @Nullable Calendar referenceTime) {
+ String text, int start, int end, @Nullable Instant referenceTime) {
final String classifiedText = text.substring(start, end);
final TextClassification.Builder builder = new TextClassification.Builder()
.setText(classifiedText);
@@ -646,7 +650,7 @@
@NonNull
public static List<LabeledIntent> create(
Context context,
- @Nullable Calendar referenceTime,
+ @Nullable Instant referenceTime,
TextClassifierImplNative.ClassificationResult classification,
String text) {
final String type = classification.getCollection().trim().toLowerCase(Locale.ENGLISH);
@@ -663,10 +667,9 @@
case TextClassifier.TYPE_DATE:
case TextClassifier.TYPE_DATE_TIME:
if (classification.getDatetimeResult() != null) {
- Calendar eventTime = Calendar.getInstance();
- eventTime.setTimeInMillis(
+ final Instant parsedTime = Instant.ofEpochMilli(
classification.getDatetimeResult().getTimeMsUtc());
- return createForDatetime(context, type, referenceTime, eventTime);
+ return createForDatetime(context, type, referenceTime, parsedTime);
} else {
return new ArrayList<>();
}
@@ -758,18 +761,17 @@
@NonNull
private static List<LabeledIntent> createForDatetime(
- Context context, String type, @Nullable Calendar referenceTime,
- Calendar eventTime) {
+ Context context, String type, @Nullable Instant referenceTime,
+ Instant parsedTime) {
if (referenceTime == null) {
// If no reference time was given, use now.
- referenceTime = Calendar.getInstance();
+ referenceTime = Instant.now();
}
List<LabeledIntent> actions = new ArrayList<>();
- actions.add(createCalendarViewIntent(context, eventTime));
- final long millisSinceReference =
- eventTime.getTimeInMillis() - referenceTime.getTimeInMillis();
- if (millisSinceReference > MIN_EVENT_FUTURE_MILLIS) {
- actions.add(createCalendarCreateEventIntent(context, eventTime, type));
+ actions.add(createCalendarViewIntent(context, parsedTime));
+ final long millisUntilEvent = referenceTime.until(parsedTime, MILLIS);
+ if (millisUntilEvent > MIN_EVENT_FUTURE_MILLIS) {
+ actions.add(createCalendarCreateEventIntent(context, parsedTime, type));
}
return actions;
}
@@ -784,10 +786,10 @@
}
@NonNull
- private static LabeledIntent createCalendarViewIntent(Context context, Calendar eventTime) {
+ private static LabeledIntent createCalendarViewIntent(Context context, Instant parsedTime) {
Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
builder.appendPath("time");
- ContentUris.appendId(builder, eventTime.getTimeInMillis());
+ ContentUris.appendId(builder, parsedTime.toEpochMilli());
return new LabeledIntent(
context.getString(com.android.internal.R.string.view_calendar),
context.getString(com.android.internal.R.string.view_calendar_desc),
@@ -796,7 +798,7 @@
@NonNull
private static LabeledIntent createCalendarCreateEventIntent(
- Context context, Calendar eventTime, @EntityType String type) {
+ Context context, Instant parsedTime, @EntityType String type) {
final boolean isAllDay = TextClassifier.TYPE_DATE.equals(type);
return new LabeledIntent(
context.getString(com.android.internal.R.string.add_calendar_event),
@@ -805,9 +807,9 @@
.setData(CalendarContract.Events.CONTENT_URI)
.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay)
.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME,
- eventTime.getTimeInMillis())
+ parsedTime.toEpochMilli())
.putExtra(CalendarContract.EXTRA_EVENT_END_TIME,
- eventTime.getTimeInMillis() + DEFAULT_EVENT_DURATION));
+ parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION));
}
}
}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index b049db3..f4b7032 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -187,11 +187,27 @@
@Override
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
+ return performBackup(packageInfo, data, /*flags=*/ 0);
+ }
+
+ @Override
+ public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data, int flags) {
+ boolean isIncremental = (flags & FLAG_INCREMENTAL) != 0;
+ boolean isNonIncremental = (flags & FLAG_NON_INCREMENTAL) != 0;
+
+ if (isIncremental) {
+ Log.i(TAG, "Performing incremental backup for " + packageInfo.packageName);
+ } else if (isNonIncremental) {
+ Log.i(TAG, "Performing non-incremental backup for " + packageInfo.packageName);
+ } else {
+ Log.i(TAG, "Performing backup for " + packageInfo.packageName);
+ }
+
if (DEBUG) {
try {
- StructStat ss = Os.fstat(data.getFileDescriptor());
- Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName
- + " size=" + ss.st_size);
+ StructStat ss = Os.fstat(data.getFileDescriptor());
+ Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName
+ + " size=" + ss.st_size + " flags=" + flags);
} catch (ErrnoException e) {
Log.w(TAG, "Unable to stat input file in performBackup() on "
+ packageInfo.packageName);
@@ -199,7 +215,26 @@
}
File packageDir = new File(mCurrentSetIncrementalDir, packageInfo.packageName);
- packageDir.mkdirs();
+ boolean hasDataForPackage = !packageDir.mkdirs();
+
+ if (isIncremental) {
+ if (mParameters.isNonIncrementalOnly() || !hasDataForPackage) {
+ if (mParameters.isNonIncrementalOnly()) {
+ Log.w(TAG, "Transport is in non-incremental only mode.");
+
+ } else {
+ Log.w(TAG,
+ "Requested incremental, but transport currently stores no data for the "
+ + "package, requesting non-incremental retry.");
+ }
+ return TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED;
+ }
+ }
+ if (isNonIncremental && hasDataForPackage) {
+ Log.w(TAG, "Requested non-incremental, deleting existing data.");
+ clearBackupData(packageInfo);
+ packageDir.mkdirs();
+ }
// Each 'record' in the restore set is kept in its own file, named by
// the record key. Wind through the data file, extracting individual
diff --git a/core/java/com/android/internal/backup/LocalTransportParameters.java b/core/java/com/android/internal/backup/LocalTransportParameters.java
index 154e79d..2427d39 100644
--- a/core/java/com/android/internal/backup/LocalTransportParameters.java
+++ b/core/java/com/android/internal/backup/LocalTransportParameters.java
@@ -26,8 +26,10 @@
private static final String TAG = "LocalTransportParams";
private static final String SETTING = Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS;
private static final String KEY_FAKE_ENCRYPTION_FLAG = "fake_encryption_flag";
+ private static final String KEY_NON_INCREMENTAL_ONLY = "non_incremental_only";
private boolean mFakeEncryptionFlag;
+ private boolean mIsNonIncrementalOnly;
LocalTransportParameters(Handler handler, ContentResolver resolver) {
super(handler, resolver, Settings.Secure.getUriFor(SETTING));
@@ -37,11 +39,16 @@
return mFakeEncryptionFlag;
}
+ boolean isNonIncrementalOnly() {
+ return mIsNonIncrementalOnly;
+ }
+
public String getSettingValue(ContentResolver resolver) {
return Settings.Secure.getString(resolver, SETTING);
}
public void update(KeyValueListParser parser) {
mFakeEncryptionFlag = parser.getBoolean(KEY_FAKE_ENCRYPTION_FLAG, false);
+ mIsNonIncrementalOnly = parser.getBoolean(KEY_NON_INCREMENTAL_ONLY, false);
}
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index cd83c57..5d40a73 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -47,6 +47,8 @@
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
+
import libcore.io.IoUtils;
/**
@@ -159,6 +161,11 @@
return null;
}
+ if (parsedArgs.apiBlacklistExemptions != null) {
+ handleApiBlacklistExemptions(parsedArgs.apiBlacklistExemptions);
+ return null;
+ }
+
if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
throw new ZygoteSecurityException("Client may not specify capabilities: " +
"permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
@@ -278,6 +285,15 @@
}
}
+ private void handleApiBlacklistExemptions(String[] exemptions) {
+ try {
+ ZygoteInit.setApiBlacklistExemptions(exemptions);
+ mSocketOutStream.writeInt(0);
+ } catch (IOException ioe) {
+ throw new IllegalStateException("Error writing to command socket", ioe);
+ }
+ }
+
protected void preload() {
ZygoteInit.lazyPreload();
}
@@ -439,6 +455,12 @@
boolean startChildZygote;
/**
+ * Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time,
+ * or when they change, via --set-api-blacklist-exemptions.
+ */
+ String[] apiBlacklistExemptions;
+
+ /**
* Constructs instance and parses args
* @param args zygote command-line args
* @throws IllegalArgumentException
@@ -592,6 +614,11 @@
preloadDefault = true;
} else if (arg.equals("--start-child-zygote")) {
startChildZygote = true;
+ } else if (arg.equals("--set-api-blacklist-exemptions")) {
+ // consume all remaining args; this is a stand-alone command, never included
+ // with the regular fork command.
+ apiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
+ curArg = args.length;
} else {
break;
}
@@ -606,7 +633,7 @@
throw new IllegalArgumentException(
"Unexpected arguments after --preload-package.");
}
- } else if (!preloadDefault) {
+ } else if (!preloadDefault && apiBlacklistExemptions == null) {
if (!seenRuntimeArgs) {
throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 9467ecc..c5d41db 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -514,6 +514,10 @@
/* should never reach here */
}
+ public static void setApiBlacklistExemptions(String[] exemptions) {
+ VMRuntime.getRuntime().setHiddenApiExemptions(exemptions);
+ }
+
/**
* Creates a PathClassLoader for the given class path that is associated with a shared
* namespace, i.e., this classloader can access platform-private native libraries. The
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 726c450..825b7a0 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -210,7 +210,7 @@
jint desiredWidth, jint desiredHeight, jobject jsubset,
jboolean requireMutable, jint allocator,
jboolean requireUnpremul, jboolean preferRamOverQuality,
- jboolean asAlphaMask) {
+ jboolean asAlphaMask, jobject jcolorSpace) {
auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
SkAndroidCodec* codec = decoder->mCodec.get();
const SkISize desiredSize = SkISize::Make(desiredWidth, desiredHeight);
@@ -264,7 +264,8 @@
// This is currently the only way to know that we should decode to F16.
colorType = codec->computeOutputColorType(colorType);
}
- sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType);
+ sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
+ colorSpace = codec->computeOutputColorSpace(colorType, colorSpace);
decodeInfo = decodeInfo.makeColorType(colorType).makeColorSpace(colorSpace);
SkBitmap bm;
@@ -507,18 +508,26 @@
return encodedFormatToString(env, decoder->mCodec->getEncodedFormat());
}
+static jobject ImageDecoder_nGetColorSpace(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+ auto* codec = reinterpret_cast<ImageDecoder*>(nativePtr)->mCodec.get();
+ auto colorType = codec->computeOutputColorType(codec->getInfo().colorType());
+ sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType);
+ return GraphicsJNI::getColorSpace(env, colorSpace, colorType);
+}
+
static const JNINativeMethod gImageDecoderMethods[] = {
{ "nCreate", "(JLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset },
{ "nCreate", "(Ljava/nio/ByteBuffer;IILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
{ "nCreate", "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
{ "nCreate", "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
{ "nCreate", "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
- { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
+ { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZLandroid/graphics/ColorSpace;)Landroid/graphics/Bitmap;",
(void*) ImageDecoder_nDecodeBitmap },
{ "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize },
{ "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding },
{ "nClose", "(J)V", (void*) ImageDecoder_nClose},
{ "nGetMimeType", "(J)Ljava/lang/String;", (void*) ImageDecoder_nGetMimeType },
+ { "nGetColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*) ImageDecoder_nGetColorSpace },
};
int register_android_graphics_ImageDecoder(JNIEnv* env) {
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index d3d6882..97abd82 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -37,6 +37,14 @@
class SkPathGlue {
public:
+ static void finalizer(SkPath* obj) {
+ // Purge entries from the HWUI path cache if this path's data is unique
+ if (obj->unique() && android::uirenderer::Caches::hasInstance()) {
+ android::uirenderer::Caches::getInstance().pathCache.removeDeferred(obj);
+ }
+ delete obj;
+ }
+
// ---------------- Regular JNI -----------------------------
static jlong init(JNIEnv* env, jclass clazz) {
@@ -48,13 +56,8 @@
return reinterpret_cast<jlong>(new SkPath(*val));
}
- static void finalize(JNIEnv* env, jclass clazz, jlong objHandle) {
- SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
- // Purge entries from the HWUI path cache if this path's data is unique
- if (obj->unique() && android::uirenderer::Caches::hasInstance()) {
- android::uirenderer::Caches::getInstance().pathCache.removeDeferred(obj);
- }
- delete obj;
+ static jlong getFinalizer(JNIEnv* env, jclass clazz) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&finalizer));
}
static void set(JNIEnv* env, jclass clazz, jlong dstHandle, jlong srcHandle) {
@@ -469,7 +472,9 @@
SkRect rect;
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
jboolean result = obj->isRect(&rect);
- GraphicsJNI::rect_to_jrectf(rect, env, jrect);
+ if (jrect) {
+ GraphicsJNI::rect_to_jrectf(rect, env, jrect);
+ }
return result;
}
@@ -510,7 +515,7 @@
static const JNINativeMethod methods[] = {
{"nInit","()J", (void*) SkPathGlue::init},
{"nInit","(J)J", (void*) SkPathGlue::init_Path},
- {"nFinalize", "(J)V", (void*) SkPathGlue::finalize},
+ {"nGetFinalizer", "()J", (void*) SkPathGlue::getFinalizer},
{"nSet","(JJ)V", (void*) SkPathGlue::set},
{"nComputeBounds","(JLandroid/graphics/RectF;)V", (void*) SkPathGlue::computeBounds},
{"nIncReserve","(JI)V", (void*) SkPathGlue::incReserve},
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index cfb8980..593747d 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -145,6 +145,7 @@
optional SettingProto transport = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto manager_constants = 5;
optional SettingProto local_transport_parameters = 6;
+ optional SettingProto packages_to_clear_data_before_full_restore = 7;
}
optional Backup backup = 10;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 48d394a..f4715fc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2016,11 +2016,11 @@
<permission android:name="android.permission.REMOVE_TASKS"
android:protectionLevel="signature" />
- <!-- @SystemApi @hide Allows an application to create/manage/remove stacks -->
+ <!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
<permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi @hide Allows an application to embed other activities -->
+ <!-- @SystemApi @TestApi @hide Allows an application to embed other activities -->
<permission android:name="android.permission.ACTIVITY_EMBEDDING"
android:protectionLevel="signature|privileged|development" />
@@ -3121,6 +3121,12 @@
<permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE"
android:protectionLevel="signature" />
+ <!-- Allows an application to control the color saturation of the display.
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.CONTROL_DISPLAY_SATURATION"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows an application to collect usage infomation about brightness slider changes.
<p>Not for use by third-party applications.</p>
@hide
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 2a3fcad..4b9465d 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -587,7 +587,8 @@
Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING,
Settings.Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT,
Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
- Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION);
+ Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION,
+ Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
@Test
public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index afc4bd5..5d58f55 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -37,9 +37,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.Calendar;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.Locale;
-import java.util.TimeZone;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -163,8 +164,9 @@
@Test
public void testParcelOptions() {
- Calendar referenceTime = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.US);
- referenceTime.setTimeInMillis(946771200000L); // 2000-01-02
+ ZonedDateTime referenceTime = ZonedDateTime.ofInstant(
+ Instant.ofEpochMilli(946771200000L), // 2000-01-02
+ ZoneId.of("UTC"));
TextClassification.Options reference = new TextClassification.Options();
reference.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY));
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 5ca0ad6..6051f88 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -432,6 +432,18 @@
public boolean isAnimated() {
return mDecoder.mAnimated;
}
+
+ /**
+ * If known, the color space the decoded bitmap will have. Note that the
+ * output color space is not guaranteed to be the color space the bitmap
+ * is encoded with. If not known (when the config is
+ * {@link Bitmap.Config#ALPHA_8} for instance), or there is an error,
+ * it is set to null.
+ */
+ @Nullable
+ public ColorSpace getColorSpace() {
+ return mDecoder.getColorSpace();
+ }
};
/** @removed
@@ -582,16 +594,17 @@
private final int mHeight;
private final boolean mAnimated;
- private int mDesiredWidth;
- private int mDesiredHeight;
- private int mAllocator = ALLOCATOR_DEFAULT;
- private boolean mRequireUnpremultiplied = false;
- private boolean mMutable = false;
- private boolean mConserveMemory = false;
- private boolean mDecodeAsAlphaMask = false;
- private Rect mCropRect;
- private Rect mOutPaddingRect;
- private Source mSource;
+ private int mDesiredWidth;
+ private int mDesiredHeight;
+ private int mAllocator = ALLOCATOR_DEFAULT;
+ private boolean mRequireUnpremultiplied = false;
+ private boolean mMutable = false;
+ private boolean mConserveMemory = false;
+ private boolean mDecodeAsAlphaMask = false;
+ private ColorSpace mDesiredColorSpace = null;
+ private Rect mCropRect;
+ private Rect mOutPaddingRect;
+ private Source mSource;
private PostProcessor mPostProcessor;
private OnPartialImageListener mOnPartialImageListener;
@@ -766,7 +779,7 @@
*
* <p>This takes an input that functions like
* {@link BitmapFactory.Options#inSampleSize}. It returns a width and
- * height that can be acheived by sampling the encoded image. Other widths
+ * height that can be achieved by sampling the encoded image. Other widths
* and heights may be supported, but will require an additional (internal)
* scaling step. Such internal scaling is *not* supported with
* {@link #setRequireUnpremultiplied} set to {@code true}.</p>
@@ -774,6 +787,8 @@
* @param sampleSize Sampling rate of the encoded image.
* @return {@link android.util.Size} of the width and height after
* sampling.
+ *
+ * @hide
*/
@NonNull
public Size getSampledSize(int sampleSize) {
@@ -789,14 +804,29 @@
}
// Modifiers
+ /** @removed
+ * @deprecated Renamed to {@link #setTargetSize}.
+ */
+ @java.lang.Deprecated
+ public ImageDecoder setResize(int width, int height) {
+ return this.setTargetSize(width, height);
+ }
+
/**
- * Resize the output to have the following size.
+ * Specify the size of the output {@link Drawable} or {@link Bitmap}.
+ *
+ * <p>By default, the output size will match the size of the encoded
+ * image, which can be retrieved from the {@link ImageInfo} in
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+ *
+ * <p>Only the last call to this or {@link #setTargetSampleSize} is
+ * respected.</p>
*
* @param width must be greater than 0.
* @param height must be greater than 0.
* @return this object for chaining.
*/
- public ImageDecoder setResize(int width, int height) {
+ public ImageDecoder setTargetSize(int width, int height) {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("Dimensions must be positive! "
+ "provided (" + width + ", " + height + ")");
@@ -807,18 +837,65 @@
return this;
}
+ /** @removed
+ * @deprecated Renamed to {@link #setTargetSampleSize}.
+ */
+ @java.lang.Deprecated
+ public ImageDecoder setResize(int sampleSize) {
+ return this.setTargetSampleSize(sampleSize);
+ }
+
+ private int getTargetDimension(int original, int sampleSize, int computed) {
+ // Sampling will never result in a smaller size than 1.
+ if (sampleSize >= original) {
+ return 1;
+ }
+
+ // Use integer divide to find the desired size. If that is what
+ // getSampledSize computed, that is the size to use.
+ int target = original / sampleSize;
+ if (computed == target) {
+ return computed;
+ }
+
+ // If sampleSize does not divide evenly into original, the decoder
+ // may round in either direction. It just needs to get a result that
+ // is close.
+ int reverse = computed * sampleSize;
+ if (Math.abs(reverse - original) < sampleSize) {
+ // This is the size that can be decoded most efficiently.
+ return computed;
+ }
+
+ // The decoder could not get close (e.g. it is a DNG image).
+ return target;
+ }
+
/**
- * Resize based on a sample size.
+ * Set the target size with a sampleSize.
*
- * <p>This has the same effect as passing the result of
- * {@link #getSampledSize} to {@link #setResize(int, int)}.</p>
+ * <p>By default, the output size will match the size of the encoded
+ * image, which can be retrieved from the {@link ImageInfo} in
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+ *
+ * <p>Requests the decoder to subsample the original image, returning a
+ * smaller image to save memory. The sample size is the number of pixels
+ * in either dimension that correspond to a single pixel in the output.
+ * For example, sampleSize == 4 returns an image that is 1/4 the
+ * width/height of the original, and 1/16 the number of pixels.</p>
+ *
+ * <p>Must be greater than or equal to 1.</p>
+ *
+ * <p>Only the last call to this or {@link #setTargetSize} is respected.</p>
*
* @param sampleSize Sampling rate of the encoded image.
* @return this object for chaining.
*/
- public ImageDecoder setResize(int sampleSize) {
+ public ImageDecoder setTargetSampleSize(int sampleSize) {
Size size = this.getSampledSize(sampleSize);
- return this.setResize(size.getWidth(), size.getHeight());
+ int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth());
+ int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight());
+ return this.setTargetSize(targetWidth, targetHeight);
}
private boolean requestedResize() {
@@ -972,8 +1049,8 @@
* Crop the output to {@code subset} of the (possibly) scaled image.
*
* <p>{@code subset} must be contained within the size set by
- * {@link #setResize} or the bounds of the image if setResize was not
- * called. Otherwise an {@link IllegalStateException} will be thrown by
+ * {@link #setTargetSize} or the bounds of the image if setTargetSize was
+ * not called. Otherwise an {@link IllegalStateException} will be thrown by
* {@link #decodeDrawable}/{@link #decodeBitmap}.</p>
*
* <p>NOT intended as a replacement for
@@ -1116,6 +1193,37 @@
return this.getDecodeAsAlphaMask();
}
+ /**
+ * Specify the desired {@link ColorSpace} for the output.
+ *
+ * <p>If non-null, the decoder will try to decode into this
+ * color space. If it is null, which is the default, or the request cannot
+ * be met, the decoder will pick either the color space embedded in the
+ * image or the color space best suited for the requested image
+ * configuration (for instance {@link ColorSpace.Named#SRGB sRGB} for
+ * the {@link Bitmap.Config#ARGB_8888} configuration).</p>
+ *
+ * <p>{@link Bitmap.Config#RGBA_F16} always uses the
+ * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space).
+ * Bitmaps in other configurations without an embedded color space are
+ * assumed to be in the {@link ColorSpace.Named#SRGB sRGB} color space.</p>
+ *
+ * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
+ * currently supported. An <code>IllegalArgumentException</code> will
+ * be thrown by the decode methods when setting a non-RGB color space
+ * such as {@link ColorSpace.Named#CIE_LAB Lab}.</p>
+ *
+ * <p class="note">The specified color space's transfer function must be
+ * an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}. An
+ * <code>IllegalArgumentException</code> will be thrown by the decode methods
+ * if calling {@link ColorSpace.Rgb#getTransferParameters()} on the
+ * specified color space returns null.</p>
+ */
+ public ImageDecoder setTargetColorSpace(ColorSpace colorSpace) {
+ mDesiredColorSpace = colorSpace;
+ return this;
+ }
+
@Override
public void close() {
mCloseGuard.close();
@@ -1154,6 +1262,17 @@
if (mPostProcessor != null && mRequireUnpremultiplied) {
throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
}
+
+ if (mDesiredColorSpace != null) {
+ if (!(mDesiredColorSpace instanceof ColorSpace.Rgb)) {
+ throw new IllegalArgumentException("The target color space must use the "
+ + "RGB color model - provided: " + mDesiredColorSpace);
+ }
+ if (((ColorSpace.Rgb) mDesiredColorSpace).getTransferParameters() == null) {
+ throw new IllegalArgumentException("The target color space must use an "
+ + "ICC parametric transfer function - provided: " + mDesiredColorSpace);
+ }
+ }
}
private static void checkSubset(int width, int height, Rect r) {
@@ -1172,7 +1291,7 @@
return nDecodeBitmap(mNativePtr, this, mPostProcessor != null,
mDesiredWidth, mDesiredHeight, mCropRect,
mMutable, mAllocator, mRequireUnpremultiplied,
- mConserveMemory, mDecodeAsAlphaMask);
+ mConserveMemory, mDecodeAsAlphaMask, mDesiredColorSpace);
}
private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
@@ -1353,7 +1472,7 @@
float scale = (float) dstDensity / srcDensity;
int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
- decoder.setResize(scaledWidth, scaledHeight);
+ decoder.setTargetSize(scaledWidth, scaledHeight);
return dstDensity;
}
@@ -1362,6 +1481,11 @@
return nGetMimeType(mNativePtr);
}
+ @Nullable
+ private ColorSpace getColorSpace() {
+ return nGetColorSpace(mNativePtr);
+ }
+
/**
* See {@link #decodeBitmap(Source, OnHeaderDecodedListener)}.
*/
@@ -1411,11 +1535,13 @@
int width, int height,
@Nullable Rect cropRect, boolean mutable,
int allocator, boolean requireUnpremul,
- boolean conserveMemory, boolean decodeAsAlphaMask)
+ boolean conserveMemory, boolean decodeAsAlphaMask,
+ @Nullable ColorSpace desiredColorSpace)
throws IOException;
private static native Size nGetSampledSize(long nativePtr,
int sampleSize);
private static native void nGetPadding(long nativePtr, @NonNull Rect outRect);
private static native void nClose(long nativePtr);
private static native String nGetMimeType(long nativePtr);
+ private static native ColorSpace nGetColorSpace(long nativePtr);
}
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 098cdc6..cd0862c 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -24,6 +24,8 @@
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
+import libcore.util.NativeAllocationRegistry;
+
/**
* The Path class encapsulates compound (multiple contour) geometric paths
* consisting of straight line segments, quadratic curves, and cubic curves.
@@ -32,10 +34,14 @@
* text on a path.
*/
public class Path {
+
+ private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ Path.class.getClassLoader(), nGetFinalizer(), 48 /* dummy size */);
+
/**
* @hide
*/
- public long mNativePath;
+ public final long mNativePath;
/**
* @hide
@@ -52,6 +58,7 @@
*/
public Path() {
mNativePath = nInit();
+ sRegistry.registerNativeAllocation(this, mNativePath);
}
/**
@@ -69,6 +76,7 @@
}
}
mNativePath = nInit(valNative);
+ sRegistry.registerNativeAllocation(this, mNativePath);
}
/**
@@ -297,7 +305,7 @@
* a rectangle
* @return true if the path specifies a rectangle
*/
- public boolean isRect(RectF rect) {
+ public boolean isRect(@Nullable RectF rect) {
return nIsRect(mNativePath, rect);
}
@@ -771,15 +779,6 @@
nTransform(mNativePath, matrix.native_instance);
}
- protected void finalize() throws Throwable {
- try {
- nFinalize(mNativePath);
- mNativePath = 0; // Other finalizers can still call us.
- } finally {
- super.finalize();
- }
- }
-
/** @hide */
public final long readOnlyNI() {
return mNativePath;
@@ -820,7 +819,7 @@
private static native long nInit();
private static native long nInit(long nPath);
- private static native void nFinalize(long nPath);
+ private static native long nGetFinalizer();
private static native void nSet(long native_dst, long nSrc);
private static native void nComputeBounds(long nPath, RectF bounds);
private static native void nIncReserve(long nPath, int extraPtCount);
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 5a8fa07..0d32075 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -33,8 +33,8 @@
boolean isUserSelectable(String alias);
void setUserSelectable(String alias, boolean isUserSelectable);
- boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
- boolean attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags,
+ int generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
+ int attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags,
out KeymasterCertificateChain chain);
boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain);
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 2daf733..46a7fa8 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -246,6 +246,82 @@
public static final String EXTRA_KEY_ACCESSIBLE = "android.security.extra.KEY_ACCESSIBLE";
/**
+ * Indicates that a call to {@link #generateKeyPair} was successful.
+ * @hide
+ */
+ public static final int KEY_GEN_SUCCESS = 0;
+
+ /**
+ * An alias was missing from the key specifications when calling {@link #generateKeyPair}.
+ * @hide
+ */
+ public static final int KEY_GEN_MISSING_ALIAS = 1;
+
+ /**
+ * A key attestation challenge was provided to {@link #generateKeyPair}, but it shouldn't
+ * have been provided.
+ * @hide
+ */
+ public static final int KEY_GEN_SUPERFLUOUS_ATTESTATION_CHALLENGE = 2;
+
+ /**
+ * Algorithm not supported by {@link #generateKeyPair}
+ * @hide
+ */
+ public static final int KEY_GEN_NO_SUCH_ALGORITHM = 3;
+
+ /**
+ * Invalid algorithm parameters when calling {@link #generateKeyPair}
+ * @hide
+ */
+ public static final int KEY_GEN_INVALID_ALGORITHM_PARAMETERS = 4;
+
+ /**
+ * Keystore is not available when calling {@link #generateKeyPair}
+ * @hide
+ */
+ public static final int KEY_GEN_NO_KEYSTORE_PROVIDER = 5;
+
+ /**
+ * General failure while calling {@link #generateKeyPair}
+ * @hide
+ */
+ public static final int KEY_GEN_FAILURE = 6;
+
+ /**
+ * Successful call to {@link #attestKey}
+ * @hide
+ */
+ public static final int KEY_ATTESTATION_SUCCESS = 0;
+
+ /**
+ * Attestation challenge missing when calling {@link #attestKey}
+ * @hide
+ */
+ public static final int KEY_ATTESTATION_MISSING_CHALLENGE = 1;
+
+ /**
+ * The caller requested Device ID attestation when calling {@link #attestKey}, but has no
+ * permissions to get device identifiers.
+ * @hide
+ */
+ public static final int KEY_ATTESTATION_CANNOT_COLLECT_DATA = 2;
+
+ /**
+ * The underlying hardware does not support Device ID attestation or cannot attest to the
+ * identifiers that are stored on the device. This indicates permanent inability
+ * to get attestation records on the device.
+ * @hide
+ */
+ public static final int KEY_ATTESTATION_CANNOT_ATTEST_IDS = 3;
+
+ /**
+ * General failure when calling {@link #attestKey}
+ * @hide
+ */
+ public static final int KEY_ATTESTATION_FAILURE = 4;
+
+ /**
* Returns an {@code Intent} that can be used for credential
* installation. The intent may be used without any extras, in
* which case the user will be able to install credentials from
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 1924bbe..1e2ebf8 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -66,6 +66,7 @@
public static final int VALUE_CORRUPTED = 8;
public static final int UNDEFINED_ACTION = 9;
public static final int WRONG_PASSWORD = 10;
+ public static final int CANNOT_ATTEST_IDS = -66;
public static final int HARDWARE_TYPE_UNAVAILABLE = -68;
/**
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 98e67c2..3643ca4 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -224,7 +224,9 @@
s.append("GNSS_KPI_END").append("\n");
GpsBatteryStats stats = mGnssPowerMetrics.getGpsBatteryStats();
if (stats != null) {
- s.append("Power Metrics").append('\n');
+ s.append("Power Metrics").append("\n");
+ s.append(" Time on battery (min): "
+ + stats.getLoggingDurationMs() / ((double) DateUtils.MINUTE_IN_MILLIS)).append("\n");
long[] t = stats.getTimeInGpsSignalQualityLevel();
if (t != null && t.length == NUM_GPS_SIGNAL_QUALITY_LEVELS) {
s.append(" Amount of time (while on battery) Top 4 Avg CN0 > " +
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index e0c567b..8d62cac 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -654,9 +654,11 @@
* ID is returned.
*
* @param level the new security level, one of
- * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE},
- * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or
- * {@link #HW_SECURE_ALL}.
+ * {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO},
+ * {@link #SECURITY_LEVEL_SW_SECURE_DECODE},
+ * {@link #SECURITY_LEVEL_HW_SECURE_CRYPTO},
+ * {@link #SECURITY_LEVEL_HW_SECURE_DECODE} or
+ * {@link #SECURITY_LEVEL_HW_SECURE_ALL}.
*
* @throws NotProvisionedException if provisioning is needed
* @throws ResourceBusyException if required resources are in use
@@ -1140,8 +1142,9 @@
* implementation.
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({SECURITY_LEVEL_UNKNOWN, SW_SECURE_CRYPTO, SW_SECURE_DECODE,
- HW_SECURE_CRYPTO, HW_SECURE_DECODE, HW_SECURE_ALL})
+ @IntDef({SECURITY_LEVEL_UNKNOWN, SECURITY_LEVEL_SW_SECURE_CRYPTO,
+ SECURITY_LEVEL_SW_SECURE_DECODE, SECURITY_LEVEL_HW_SECURE_CRYPTO,
+ SECURITY_LEVEL_HW_SECURE_DECODE, SECURITY_LEVEL_HW_SECURE_ALL})
public @interface SecurityLevel {}
/**
@@ -1153,31 +1156,31 @@
/**
* DRM key management uses software-based whitebox crypto.
*/
- public static final int SW_SECURE_CRYPTO = 1;
+ public static final int SECURITY_LEVEL_SW_SECURE_CRYPTO = 1;
/**
* DRM key management and decoding use software-based whitebox crypto.
*/
- public static final int SW_SECURE_DECODE = 2;
+ public static final int SECURITY_LEVEL_SW_SECURE_DECODE = 2;
/**
* DRM key management and crypto operations are performed within a hardware
* backed trusted execution environment.
*/
- public static final int HW_SECURE_CRYPTO = 3;
+ public static final int SECURITY_LEVEL_HW_SECURE_CRYPTO = 3;
/**
* DRM key management, crypto operations and decoding of content are
* performed within a hardware backed trusted execution environment.
*/
- public static final int HW_SECURE_DECODE = 4;
+ public static final int SECURITY_LEVEL_HW_SECURE_DECODE = 4;
/**
* DRM key management, crypto operations, decoding of content and all
* handling of the media (compressed and uncompressed) is handled within a
* hardware backed trusted execution environment.
*/
- public static final int HW_SECURE_ALL = 5;
+ public static final int SECURITY_LEVEL_HW_SECURE_ALL = 5;
/**
* The maximum security level supported by the device. This is the default
@@ -1203,9 +1206,9 @@
* @param sessionId the session to query.
* <p>
* @return one of {@link #SECURITY_LEVEL_UNKNOWN},
- * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE},
- * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or
- * {@link #HW_SECURE_ALL}.
+ * {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO}, {@link #SECURITY_LEVEL_SW_SECURE_DECODE},
+ * {@link #SECURITY_LEVEL_HW_SECURE_CRYPTO}, {@link #SECURITY_LEVEL_HW_SECURE_DECODE} or
+ * {@link #SECURITY_LEVEL_HW_SECURE_ALL}.
*/
@SecurityLevel
public native int getSecurityLevel(@NonNull byte[] sessionId);
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 3affee5c0..bd0019f 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -50,6 +50,4 @@
void setPlaybackToLocal(in AudioAttributes attributes);
void setPlaybackToRemote(int control, int max);
void setCurrentVolume(int currentVolume);
-
- String getCallingPackage();
}
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 893bd3c..9634c7f 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -25,30 +25,33 @@
* @hide
*/
oneway interface ISessionCallback {
- void onCommand(String command, in Bundle args, in ResultReceiver cb);
- void onMediaButton(in Intent mediaButtonIntent, int sequenceNumber, in ResultReceiver cb);
+ void onCommand(String packageName, int pid, int uid, String command, in Bundle args,
+ in ResultReceiver cb);
+ void onMediaButton(String packageName, int pid, int uid, in Intent mediaButtonIntent,
+ int sequenceNumber, in ResultReceiver cb);
// These callbacks are for the TransportPerformer
- void onPrepare();
- void onPrepareFromMediaId(String mediaId, in Bundle extras);
- void onPrepareFromSearch(String query, in Bundle extras);
- void onPrepareFromUri(in Uri uri, in Bundle extras);
- void onPlay();
- void onPlayFromMediaId(String mediaId, in Bundle extras);
- void onPlayFromSearch(String query, in Bundle extras);
- void onPlayFromUri(in Uri uri, in Bundle extras);
- void onSkipToTrack(long id);
- void onPause();
- void onStop();
- void onNext();
- void onPrevious();
- void onFastForward();
- void onRewind();
- void onSeekTo(long pos);
- void onRate(in Rating rating);
- void onCustomAction(String action, in Bundle args);
+ void onPrepare(String packageName, int pid, int uid);
+ void onPrepareFromMediaId(String packageName, int pid, int uid, String mediaId,
+ in Bundle extras);
+ void onPrepareFromSearch(String packageName, int pid, int uid, String query, in Bundle extras);
+ void onPrepareFromUri(String packageName, int pid, int uid, in Uri uri, in Bundle extras);
+ void onPlay(String packageName, int pid, int uid);
+ void onPlayFromMediaId(String packageName, int pid, int uid, String mediaId, in Bundle extras);
+ void onPlayFromSearch(String packageName, int pid, int uid, String query, in Bundle extras);
+ void onPlayFromUri(String packageName, int pid, int uid, in Uri uri, in Bundle extras);
+ void onSkipToTrack(String packageName, int pid, int uid, long id);
+ void onPause(String packageName, int pid, int uid);
+ void onStop(String packageName, int pid, int uid);
+ void onNext(String packageName, int pid, int uid);
+ void onPrevious(String packageName, int pid, int uid);
+ void onFastForward(String packageName, int pid, int uid);
+ void onRewind(String packageName, int pid, int uid);
+ void onSeekTo(String packageName, int pid, int uid, long pos);
+ void onRate(String packageName, int pid, int uid, in Rating rating);
+ void onCustomAction(String packageName, int pid, int uid, String action, in Bundle args);
// These callbacks are for volume handling
- void onAdjustVolume(int direction);
- void onSetVolumeTo(int value);
+ void onAdjustVolume(String packageName, int pid, int uid, int direction);
+ void onSetVolumeTo(String packageName, int pid, int uid, int value);
}
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 249bcdc..06f5863 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -36,8 +36,8 @@
* @hide
*/
interface ISessionController {
- void sendCommand(String command, in Bundle args, in ResultReceiver cb);
- boolean sendMediaButton(in KeyEvent mediaButton);
+ void sendCommand(String packageName, String command, in Bundle args, in ResultReceiver cb);
+ boolean sendMediaButton(String packageName, in KeyEvent mediaButton);
void registerCallbackListener(in ISessionControllerCallback cb);
void unregisterCallbackListener(in ISessionControllerCallback cb);
boolean isTransportControlEnabled();
@@ -46,28 +46,28 @@
PendingIntent getLaunchPendingIntent();
long getFlags();
ParcelableVolumeInfo getVolumeAttributes();
- void adjustVolume(int direction, int flags, String packageName);
- void setVolumeTo(int value, int flags, String packageName);
+ void adjustVolume(String packageName, int direction, int flags);
+ void setVolumeTo(String packageName, int value, int flags);
// These commands are for the TransportControls
- void prepare();
- void prepareFromMediaId(String mediaId, in Bundle extras);
- void prepareFromSearch(String string, in Bundle extras);
- void prepareFromUri(in Uri uri, in Bundle extras);
- void play();
- void playFromMediaId(String mediaId, in Bundle extras);
- void playFromSearch(String string, in Bundle extras);
- void playFromUri(in Uri uri, in Bundle extras);
- void skipToQueueItem(long id);
- void pause();
- void stop();
- void next();
- void previous();
- void fastForward();
- void rewind();
- void seekTo(long pos);
- void rate(in Rating rating);
- void sendCustomAction(String action, in Bundle args);
+ void prepare(String packageName);
+ void prepareFromMediaId(String packageName, String mediaId, in Bundle extras);
+ void prepareFromSearch(String packageName, String string, in Bundle extras);
+ void prepareFromUri(String packageName, in Uri uri, in Bundle extras);
+ void play(String packageName);
+ void playFromMediaId(String packageName, String mediaId, in Bundle extras);
+ void playFromSearch(String packageName, String string, in Bundle extras);
+ void playFromUri(String packageName, in Uri uri, in Bundle extras);
+ void skipToQueueItem(String packageName, long id);
+ void pause(String packageName);
+ void stop(String packageName);
+ void next(String packageName);
+ void previous(String packageName);
+ void fastForward(String packageName);
+ void rewind(String packageName);
+ void seekTo(String packageName, long pos);
+ void rate(String packageName, in Rating rating);
+ void sendCustomAction(String packageName, String action, in Bundle args);
MediaMetadata getMetadata();
PlaybackState getPlaybackState();
ParceledListSlice getQueue();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 622900f..f16804c 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -133,7 +133,7 @@
return false;
}
try {
- return mSessionBinder.sendMediaButton(keyEvent);
+ return mSessionBinder.sendMediaButton(mContext.getPackageName(), keyEvent);
} catch (RemoteException e) {
// System is dead. =(
}
@@ -301,7 +301,7 @@
*/
public void setVolumeTo(int value, int flags) {
try {
- mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName());
+ mSessionBinder.setVolumeTo(mContext.getPackageName(), value, flags);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling setVolumeTo.", e);
}
@@ -322,7 +322,7 @@
*/
public void adjustVolume(int direction, int flags) {
try {
- mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName());
+ mSessionBinder.adjustVolume(mContext.getPackageName(), direction, flags);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
}
@@ -388,7 +388,7 @@
throw new IllegalArgumentException("command cannot be null or empty");
}
try {
- mSessionBinder.sendCommand(command, args, cb);
+ mSessionBinder.sendCommand(mContext.getPackageName(), command, args, cb);
} catch (RemoteException e) {
Log.d(TAG, "Dead object in sendCommand.", e);
}
@@ -600,7 +600,7 @@
*/
public void prepare() {
try {
- mSessionBinder.prepare();
+ mSessionBinder.prepare(mContext.getPackageName());
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling prepare.", e);
}
@@ -624,7 +624,7 @@
"You must specify a non-empty String for prepareFromMediaId.");
}
try {
- mSessionBinder.prepareFromMediaId(mediaId, extras);
+ mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mediaId, extras);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e);
}
@@ -650,7 +650,7 @@
query = "";
}
try {
- mSessionBinder.prepareFromSearch(query, extras);
+ mSessionBinder.prepareFromSearch(mContext.getPackageName(), query, extras);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling prepare(" + query + ").", e);
}
@@ -674,7 +674,7 @@
"You must specify a non-empty Uri for prepareFromUri.");
}
try {
- mSessionBinder.prepareFromUri(uri, extras);
+ mSessionBinder.prepareFromUri(mContext.getPackageName(), uri, extras);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling prepare(" + uri + ").", e);
}
@@ -685,7 +685,7 @@
*/
public void play() {
try {
- mSessionBinder.play();
+ mSessionBinder.play(mContext.getPackageName());
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling play.", e);
}
@@ -704,7 +704,7 @@
"You must specify a non-empty String for playFromMediaId.");
}
try {
- mSessionBinder.playFromMediaId(mediaId, extras);
+ mSessionBinder.playFromMediaId(mContext.getPackageName(), mediaId, extras);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
}
@@ -726,7 +726,7 @@
query = "";
}
try {
- mSessionBinder.playFromSearch(query, extras);
+ mSessionBinder.playFromSearch(mContext.getPackageName(), query, extras);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling play(" + query + ").", e);
}
@@ -745,7 +745,7 @@
"You must specify a non-empty Uri for playFromUri.");
}
try {
- mSessionBinder.playFromUri(uri, extras);
+ mSessionBinder.playFromUri(mContext.getPackageName(), uri, extras);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling play(" + uri + ").", e);
}
@@ -757,7 +757,7 @@
*/
public void skipToQueueItem(long id) {
try {
- mSessionBinder.skipToQueueItem(id);
+ mSessionBinder.skipToQueueItem(mContext.getPackageName(), id);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
}
@@ -769,7 +769,7 @@
*/
public void pause() {
try {
- mSessionBinder.pause();
+ mSessionBinder.pause(mContext.getPackageName());
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling pause.", e);
}
@@ -781,7 +781,7 @@
*/
public void stop() {
try {
- mSessionBinder.stop();
+ mSessionBinder.stop(mContext.getPackageName());
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling stop.", e);
}
@@ -794,7 +794,7 @@
*/
public void seekTo(long pos) {
try {
- mSessionBinder.seekTo(pos);
+ mSessionBinder.seekTo(mContext.getPackageName(), pos);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling seekTo.", e);
}
@@ -806,7 +806,7 @@
*/
public void fastForward() {
try {
- mSessionBinder.fastForward();
+ mSessionBinder.fastForward(mContext.getPackageName());
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling fastForward.", e);
}
@@ -817,7 +817,7 @@
*/
public void skipToNext() {
try {
- mSessionBinder.next();
+ mSessionBinder.next(mContext.getPackageName());
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling next.", e);
}
@@ -829,7 +829,7 @@
*/
public void rewind() {
try {
- mSessionBinder.rewind();
+ mSessionBinder.rewind(mContext.getPackageName());
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling rewind.", e);
}
@@ -840,7 +840,7 @@
*/
public void skipToPrevious() {
try {
- mSessionBinder.previous();
+ mSessionBinder.previous(mContext.getPackageName());
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling previous.", e);
}
@@ -855,7 +855,7 @@
*/
public void setRating(Rating rating) {
try {
- mSessionBinder.rate(rating);
+ mSessionBinder.rate(mContext.getPackageName(), rating);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling rate.", e);
}
@@ -890,7 +890,7 @@
throw new IllegalArgumentException("CustomAction cannot be null.");
}
try {
- mSessionBinder.sendCustomAction(action, args);
+ mSessionBinder.sendCustomAction(mContext.getPackageName(), action, args);
} catch (RemoteException e) {
Log.d(TAG, "Dead object in sendCustomAction.", e);
}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index b8d01c4..5e8b8ca 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -39,6 +39,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
import android.service.media.MediaBrowserService;
import android.text.TextUtils;
import android.util.Log;
@@ -103,6 +104,16 @@
*/
public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16;
+ /**
+ * @hide
+ */
+ public static final int INVALID_UID = -1;
+
+ /**
+ * @hide
+ */
+ public static final int INVALID_PID = -1;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {
@@ -501,6 +512,22 @@
}
/**
+ * Gets the controller information who sent the current request.
+ * <p>
+ * Note: This is only valid while in a request callback, such as {@link Callback#onPlay}.
+ *
+ * @throws IllegalStateException If this method is called outside of {@link Callback} methods.
+ * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
+ */
+ public final @NonNull RemoteUserInfo getCurrentControllerInfo() {
+ if (mCallback == null || mCallback.mCurrentControllerInfo == null) {
+ throw new IllegalStateException(
+ "This should be called inside of MediaSession.Callback methods");
+ }
+ return mCallback.mCurrentControllerInfo;
+ }
+
+ /**
* Notify the system that the remote volume changed.
*
* @param provider The provider that is handling volume changes.
@@ -528,16 +555,14 @@
* @hide
*/
public String getCallingPackage() {
- try {
- return mBinder.getCallingPackage();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Dead object in getCallingPackage.", e);
+ if (mCallback != null) {
+ return mCallback.mCurrentControllerInfo.getPackageName();
}
return null;
}
- private void dispatchPrepare() {
- postToCallback(CallbackMessageHandler.MSG_PREPARE);
+ private void dispatchPrepare(Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_PREPARE, null, extras);
}
private void dispatchPrepareFromMediaId(String mediaId, Bundle extras) {
@@ -552,8 +577,8 @@
postToCallback(CallbackMessageHandler.MSG_PREPARE_URI, uri, extras);
}
- private void dispatchPlay() {
- postToCallback(CallbackMessageHandler.MSG_PLAY);
+ private void dispatchPlay(Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_PLAY, null, extras);
}
private void dispatchPlayFromMediaId(String mediaId, Bundle extras) {
@@ -568,69 +593,61 @@
postToCallback(CallbackMessageHandler.MSG_PLAY_URI, uri, extras);
}
- private void dispatchSkipToItem(long id) {
- postToCallback(CallbackMessageHandler.MSG_SKIP_TO_ITEM, id);
+ private void dispatchSkipToItem(long id, Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_SKIP_TO_ITEM, id, extras);
}
- private void dispatchPause() {
- postToCallback(CallbackMessageHandler.MSG_PAUSE);
+ private void dispatchPause(Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_PAUSE, null, extras);
}
- private void dispatchStop() {
- postToCallback(CallbackMessageHandler.MSG_STOP);
+ private void dispatchStop(Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_STOP, null, extras);
}
- private void dispatchNext() {
- postToCallback(CallbackMessageHandler.MSG_NEXT);
+ private void dispatchNext(Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_NEXT, null, extras);
}
- private void dispatchPrevious() {
- postToCallback(CallbackMessageHandler.MSG_PREVIOUS);
+ private void dispatchPrevious(Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_PREVIOUS, null, extras);
}
- private void dispatchFastForward() {
- postToCallback(CallbackMessageHandler.MSG_FAST_FORWARD);
+ private void dispatchFastForward(Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_FAST_FORWARD, null, extras);
}
- private void dispatchRewind() {
- postToCallback(CallbackMessageHandler.MSG_REWIND);
+ private void dispatchRewind(Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_REWIND, null, extras);
}
- private void dispatchSeekTo(long pos) {
- postToCallback(CallbackMessageHandler.MSG_SEEK_TO, pos);
+ private void dispatchSeekTo(long pos, Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_SEEK_TO, pos, extras);
}
- private void dispatchRate(Rating rating) {
- postToCallback(CallbackMessageHandler.MSG_RATE, rating);
+ private void dispatchRate(Rating rating, Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_RATE, rating, extras);
}
- private void dispatchCustomAction(String action, Bundle args) {
- postToCallback(CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
+ private void dispatchCustomAction(String action, Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_CUSTOM_ACTION, action, extras);
}
- private void dispatchMediaButton(Intent mediaButtonIntent) {
- postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent);
+ private void dispatchMediaButton(Intent mediaButtonIntent, Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent, extras);
}
- private void dispatchAdjustVolume(int direction) {
- postToCallback(CallbackMessageHandler.MSG_ADJUST_VOLUME, direction);
+ private void dispatchAdjustVolume(int direction, Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_ADJUST_VOLUME, direction, extras);
}
- private void dispatchSetVolumeTo(int volume) {
- postToCallback(CallbackMessageHandler.MSG_SET_VOLUME, volume);
+ private void dispatchSetVolumeTo(int volume, Bundle extras) {
+ postToCallback(CallbackMessageHandler.MSG_SET_VOLUME, volume, extras);
}
- private void postToCallback(int what) {
- postToCallback(what, null);
- }
-
- private void postCommand(String command, Bundle args, ResultReceiver resultCb) {
+ private void postCommand(String command, Bundle args, ResultReceiver resultCb, Bundle extras) {
Command cmd = new Command(command, args, resultCb);
- postToCallback(CallbackMessageHandler.MSG_COMMAND, cmd);
- }
-
- private void postToCallback(int what, Object obj) {
- postToCallback(what, obj, null);
+ postToCallback(CallbackMessageHandler.MSG_COMMAND, cmd, extras);
}
private void postToCallback(int what, Object obj, Bundle extras) {
@@ -734,9 +751,13 @@
* and the system. A callback may be set using {@link #setCallback}.
*/
public abstract static class Callback {
+
private MediaSession mSession;
private CallbackMessageHandler mHandler;
private boolean mMediaPlayPauseKeyPending;
+ private String mCallingPackage;
+ private int mCallingPid;
+ private int mCallingUid;
public Callback() {
}
@@ -1023,24 +1044,26 @@
private WeakReference<MediaSession> mMediaSession;
public CallbackStub(MediaSession session) {
- mMediaSession = new WeakReference<MediaSession>(session);
+ mMediaSession = new WeakReference<>(session);
}
@Override
- public void onCommand(String command, Bundle args, ResultReceiver cb) {
+ public void onCommand(String packageName, int pid, int uid, String command, Bundle args,
+ ResultReceiver cb) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.postCommand(command, args, cb);
+ session.postCommand(command, args, cb, createExtraBundle(packageName, pid, uid));
}
}
@Override
- public void onMediaButton(Intent mediaButtonIntent, int sequenceNumber,
- ResultReceiver cb) {
+ public void onMediaButton(String packageName, int pid, int uid, Intent mediaButtonIntent,
+ int sequenceNumber, ResultReceiver cb) {
MediaSession session = mMediaSession.get();
try {
if (session != null) {
- session.dispatchMediaButton(mediaButtonIntent);
+ session.dispatchMediaButton(
+ mediaButtonIntent, createExtraBundle(packageName, pid, uid));
}
} finally {
if (cb != null) {
@@ -1050,165 +1073,191 @@
}
@Override
- public void onPrepare() {
+ public void onPrepare(String packageName, int pid, int uid) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchPrepare();
+ session.dispatchPrepare(createExtraBundle(packageName, pid, uid));
}
}
@Override
- public void onPrepareFromMediaId(String mediaId, Bundle extras) {
+ public void onPrepareFromMediaId(String packageName, int pid, int uid, String mediaId,
+ Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchPrepareFromMediaId(mediaId, extras);
+ session.dispatchPrepareFromMediaId(
+ mediaId, createExtraBundle(packageName, pid, uid, extras));
}
}
@Override
- public void onPrepareFromSearch(String query, Bundle extras) {
+ public void onPrepareFromSearch(String packageName, int pid, int uid, String query,
+ Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchPrepareFromSearch(query, extras);
+ session.dispatchPrepareFromSearch(
+ query, createExtraBundle(packageName, pid, uid, extras));
}
}
@Override
- public void onPrepareFromUri(Uri uri, Bundle extras) {
+ public void onPrepareFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchPrepareFromUri(uri, extras);
+ session.dispatchPrepareFromUri(uri,
+ createExtraBundle(packageName, pid, uid, extras));
}
}
@Override
- public void onPlay() {
+ public void onPlay(String packageName, int pid, int uid) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchPlay();
+ session.dispatchPlay(createExtraBundle(packageName, pid, uid));
}
}
@Override
- public void onPlayFromMediaId(String mediaId, Bundle extras) {
+ public void onPlayFromMediaId(String packageName, int pid, int uid, String mediaId,
+ Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchPlayFromMediaId(mediaId, extras);
+ session.dispatchPlayFromMediaId(
+ mediaId, createExtraBundle(packageName, pid, uid, extras));
}
}
@Override
- public void onPlayFromSearch(String query, Bundle extras) {
+ public void onPlayFromSearch(String packageName, int pid, int uid, String query,
+ Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchPlayFromSearch(query, extras);
+ session.dispatchPlayFromSearch(query, createExtraBundle(packageName, pid, uid,
+ extras));
}
}
@Override
- public void onPlayFromUri(Uri uri, Bundle extras) {
+ public void onPlayFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchPlayFromUri(uri, extras);
+ session.dispatchPlayFromUri(uri, createExtraBundle(packageName, pid, uid, extras));
}
}
@Override
- public void onSkipToTrack(long id) {
+ public void onSkipToTrack(String packageName, int pid, int uid, long id) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchSkipToItem(id);
+ session.dispatchSkipToItem(id, createExtraBundle(packageName, pid, uid));
}
}
@Override
- public void onPause() {
+ public void onPause(String packageName, int pid, int uid) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchPause();
+ session.dispatchPause(createExtraBundle(packageName, pid, uid));
}
}
@Override
- public void onStop() {
+ public void onStop(String packageName, int pid, int uid) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchStop();
+ session.dispatchStop(createExtraBundle(packageName, pid, uid));
}
}
@Override
- public void onNext() {
+ public void onNext(String packageName, int pid, int uid) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchNext();
+ session.dispatchNext(createExtraBundle(packageName, pid, uid));
}
}
@Override
- public void onPrevious() {
+ public void onPrevious(String packageName, int pid, int uid) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchPrevious();
+ session.dispatchPrevious(createExtraBundle(packageName, pid, uid));
}
}
@Override
- public void onFastForward() {
+ public void onFastForward(String packageName, int pid, int uid) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchFastForward();
+ session.dispatchFastForward(createExtraBundle(packageName, pid, uid));
}
}
@Override
- public void onRewind() {
+ public void onRewind(String packageName, int pid, int uid) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchRewind();
+ session.dispatchRewind(createExtraBundle(packageName, pid, uid));
}
}
@Override
- public void onSeekTo(long pos) {
+ public void onSeekTo(String packageName, int pid, int uid, long pos) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchSeekTo(pos);
+ session.dispatchSeekTo(pos, createExtraBundle(packageName, pid, uid));
}
}
@Override
- public void onRate(Rating rating) {
+ public void onRate(String packageName, int pid, int uid, Rating rating) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchRate(rating);
+ session.dispatchRate(rating, createExtraBundle(packageName, pid, uid));
}
}
@Override
- public void onCustomAction(String action, Bundle args) {
+ public void onCustomAction(String packageName, int pid, int uid, String action,
+ Bundle args) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchCustomAction(action, args);
+ session.dispatchCustomAction(
+ action, createExtraBundle(packageName, pid, uid, args));
}
}
@Override
- public void onAdjustVolume(int direction) {
+ public void onAdjustVolume(String packageName, int pid, int uid, int direction) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchAdjustVolume(direction);
+ session.dispatchAdjustVolume(direction, createExtraBundle(packageName, pid, uid));
}
}
@Override
- public void onSetVolumeTo(int value) {
+ public void onSetVolumeTo(String packageName, int pid, int uid, int value) {
MediaSession session = mMediaSession.get();
if (session != null) {
- session.dispatchSetVolumeTo(value);
+ session.dispatchSetVolumeTo(value, createExtraBundle(packageName, pid, uid));
}
}
+ private Bundle createExtraBundle(String packageName, int pid, int uid) {
+ return createExtraBundle(packageName, pid, uid, null);
+ }
+
+ private Bundle createExtraBundle(String packageName, int pid, int uid,
+ Bundle originalBundle) {
+ Bundle bundle = new Bundle();
+ bundle.putString(CallbackMessageHandler.EXTRA_KEY_CALLING_PACKAGE, packageName);
+ bundle.putInt(CallbackMessageHandler.EXTRA_KEY_CALLING_PID, pid);
+ bundle.putInt(CallbackMessageHandler.EXTRA_KEY_CALLING_UID, uid);
+ if (originalBundle != null) {
+ bundle.putBundle(CallbackMessageHandler.EXTRA_KEY_ORIGINAL_BUNDLE, originalBundle);
+ }
+ return bundle;
+ }
}
/**
@@ -1272,7 +1321,8 @@
return 0;
}
- public static final Creator<MediaSession.QueueItem> CREATOR = new Creator<MediaSession.QueueItem>() {
+ public static final Creator<MediaSession.QueueItem> CREATOR =
+ new Creator<MediaSession.QueueItem>() {
@Override
public MediaSession.QueueItem createFromParcel(Parcel p) {
@@ -1329,6 +1379,15 @@
private class CallbackMessageHandler extends Handler {
+ private static final String EXTRA_KEY_CALLING_PACKAGE =
+ "android.media.session.extra.CALLING_PACKAGE";
+ private static final String EXTRA_KEY_CALLING_PID =
+ "android.media.session.extra.CALLING_PID";
+ private static final String EXTRA_KEY_CALLING_UID =
+ "android.media.session.extra.CALLING_UID";
+ private static final String EXTRA_KEY_ORIGINAL_BUNDLE =
+ "android.media.session.extra.ORIGINAL_BUNDLE";
+
private static final int MSG_COMMAND = 1;
private static final int MSG_MEDIA_BUTTON = 2;
private static final int MSG_PREPARE = 3;
@@ -1355,6 +1414,8 @@
private MediaSession.Callback mCallback;
+ private RemoteUserInfo mCurrentControllerInfo;
+
public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) {
super(looper, null, true);
mCallback = callback;
@@ -1367,21 +1428,17 @@
msg.sendToTarget();
}
- public void post(int what, Object obj) {
- obtainMessage(what, obj).sendToTarget();
- }
-
- public void post(int what) {
- post(what, null);
- }
-
- public void post(int what, Object obj, int arg1) {
- obtainMessage(what, arg1, 0, obj).sendToTarget();
- }
-
@Override
public void handleMessage(Message msg) {
VolumeProvider vp;
+ Bundle bundle = msg.getData();
+ Bundle originalBundle = bundle.getBundle(EXTRA_KEY_ORIGINAL_BUNDLE);
+
+ mCurrentControllerInfo = new RemoteUserInfo(
+ bundle.getString(EXTRA_KEY_CALLING_PACKAGE),
+ bundle.getInt(EXTRA_KEY_CALLING_PID, INVALID_PID),
+ bundle.getInt(EXTRA_KEY_CALLING_UID, INVALID_UID));
+
switch (msg.what) {
case MSG_COMMAND:
Command cmd = (Command) msg.obj;
@@ -1394,25 +1451,25 @@
mCallback.onPrepare();
break;
case MSG_PREPARE_MEDIA_ID:
- mCallback.onPrepareFromMediaId((String) msg.obj, msg.getData());
+ mCallback.onPrepareFromMediaId((String) msg.obj, originalBundle);
break;
case MSG_PREPARE_SEARCH:
- mCallback.onPrepareFromSearch((String) msg.obj, msg.getData());
+ mCallback.onPrepareFromSearch((String) msg.obj, originalBundle);
break;
case MSG_PREPARE_URI:
- mCallback.onPrepareFromUri((Uri) msg.obj, msg.getData());
+ mCallback.onPrepareFromUri((Uri) msg.obj, originalBundle);
break;
case MSG_PLAY:
mCallback.onPlay();
break;
case MSG_PLAY_MEDIA_ID:
- mCallback.onPlayFromMediaId((String) msg.obj, msg.getData());
+ mCallback.onPlayFromMediaId((String) msg.obj, originalBundle);
break;
case MSG_PLAY_SEARCH:
- mCallback.onPlayFromSearch((String) msg.obj, msg.getData());
+ mCallback.onPlayFromSearch((String) msg.obj, originalBundle);
break;
case MSG_PLAY_URI:
- mCallback.onPlayFromUri((Uri) msg.obj, msg.getData());
+ mCallback.onPlayFromUri((Uri) msg.obj, originalBundle);
break;
case MSG_SKIP_TO_ITEM:
mCallback.onSkipToQueueItem((Long) msg.obj);
@@ -1442,7 +1499,7 @@
mCallback.onSetRating((Rating) msg.obj);
break;
case MSG_CUSTOM_ACTION:
- mCallback.onCustomAction((String) msg.obj, msg.getData());
+ mCallback.onCustomAction((String) msg.obj, originalBundle);
break;
case MSG_ADJUST_VOLUME:
synchronized (mLock) {
@@ -1464,6 +1521,7 @@
mCallback.handleMediaPlayPauseKeySingleTapIfPending();
break;
}
+ mCurrentControllerInfo = null;
}
}
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 3b12fca..519af1b 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -37,7 +37,9 @@
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.service.media.MediaBrowserService;
import android.service.notification.NotificationListenerService;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.view.KeyEvent;
@@ -340,19 +342,20 @@
}
/**
- * Returns whether the api
+ * Returns whether the app is trusted.
+ * <p>
+ * An app is trusted if the app holds the android.Manifest.permission.MEDIA_CONTENT_CONTROL
+ * permission or has an enabled notification listener.
*
- * @param packageName packageName
- * @param pid pid of the app
- * @param uid uid of the app
- * @hide
+ * @param userInfo The remote user info
*/
- public boolean isTrusted(@NonNull String packageName, int pid, int uid) {
- if (packageName == null) {
+ public boolean isTrustedForMediaControl(RemoteUserInfo userInfo) {
+ if (userInfo.getPackageName() == null) {
return false;
}
try {
- return mService.isTrusted(packageName, pid, uid);
+ return mService.isTrusted(
+ userInfo.getPackageName(), userInfo.getPid(), userInfo.getUid());
} catch (RemoteException e) {
Log.wtf(TAG, "Cannot communicate with the service.", e);
}
@@ -763,6 +766,56 @@
public abstract void onAddressedPlayerChanged(ComponentName mediaButtonReceiver);
}
+ /**
+ * Information of a remote user of {@link MediaSession} or {@link MediaBrowserService}.
+ * This can be used to decide whether the remote user is trusted app.
+ *
+ * @see #isTrustedForMediaControl(RemoteUserInfo)
+ */
+ public static final class RemoteUserInfo {
+ private String mPackageName;
+ private int mPid;
+ private int mUid;
+
+ public RemoteUserInfo(String packageName, int pid, int uid) {
+ mPackageName = packageName;
+ mPid = pid;
+ mUid = uid;
+ }
+
+ /**
+ * @return package name of the controller
+ */
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * @return pid of the controller
+ */
+ public int getPid() {
+ return mPid;
+ }
+
+ /**
+ * @return uid of the controller
+ */
+ public int getUid() {
+ return mUid;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof RemoteUserInfo)) {
+ return false;
+ }
+ RemoteUserInfo otherUserInfo = (RemoteUserInfo) obj;
+ return TextUtils.equals(mPackageName, otherUserInfo.mPackageName)
+ && mPid == otherUserInfo.mPid
+ && mUid == otherUserInfo.mUid;
+ }
+ }
+
private static final class SessionsChangedWrapper {
private Context mContext;
private OnActiveSessionsChangedListener mListener;
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 4fc43ea..6d1bd45 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -31,6 +31,8 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -112,6 +114,8 @@
*/
private class ConnectionRecord implements IBinder.DeathRecipient {
String pkg;
+ int uid;
+ int pid;
Bundle rootHints;
IMediaBrowserServiceCallbacks callbacks;
BrowserRoot root;
@@ -199,6 +203,7 @@
public void connect(final String pkg, final Bundle rootHints,
final IMediaBrowserServiceCallbacks callbacks) {
+ final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
if (!isValidPackage(pkg, uid)) {
throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
@@ -215,9 +220,14 @@
final ConnectionRecord connection = new ConnectionRecord();
connection.pkg = pkg;
+ connection.pid = pid;
+ connection.uid = uid;
connection.rootHints = rootHints;
connection.callbacks = callbacks;
+
+ mCurConnection = connection;
connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
+ mCurConnection = null;
// If they didn't return something, don't allow this client.
if (connection.root == null) {
@@ -505,21 +515,36 @@
* media browser service when connecting and retrieving the root id for browsing, or null if
* none. The contents of this bundle may affect the information returned when browsing.
*
- * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren} or
- * {@link #onLoadItem}.
+ * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or
+ * {@link #onLoadChildren} or {@link #onLoadItem}.
* @see MediaBrowserService.BrowserRoot#EXTRA_RECENT
* @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE
* @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
*/
public final Bundle getBrowserRootHints() {
if (mCurConnection == null) {
- throw new IllegalStateException("This should be called inside of onLoadChildren or"
- + " onLoadItem methods");
+ throw new IllegalStateException("This should be called inside of onGetRoot or"
+ + " onLoadChildren or onLoadItem methods");
}
return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
}
/**
+ * Gets the browser information who sent the current request.
+ *
+ * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or
+ * {@link #onLoadChildren} or {@link #onLoadItem}.
+ * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
+ */
+ public final RemoteUserInfo getCurrentBrowserInfo() {
+ if (mCurConnection == null) {
+ throw new IllegalStateException("This should be called inside of onGetRoot or"
+ + " onLoadChildren or onLoadItem methods");
+ }
+ return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid);
+ }
+
+ /**
* Notifies all connected media browsers that the children of
* the specified parent id have changed in some way.
* This will cause browsers to fetch subscribed content again.
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 4c20f05..54541f0 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -729,15 +729,15 @@
GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_UNKNOWN", "I");
gSecurityLevels.kSecurityLevelUnknown = env->GetStaticIntField(clazz, field);
- GET_STATIC_FIELD_ID(field, clazz, "SW_SECURE_CRYPTO", "I");
+ GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_SW_SECURE_CRYPTO", "I");
gSecurityLevels.kSecurityLevelSwSecureCrypto = env->GetStaticIntField(clazz, field);
- GET_STATIC_FIELD_ID(field, clazz, "SW_SECURE_DECODE", "I");
+ GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_SW_SECURE_DECODE", "I");
gSecurityLevels.kSecurityLevelSwSecureDecode = env->GetStaticIntField(clazz, field);
- GET_STATIC_FIELD_ID(field, clazz, "HW_SECURE_CRYPTO", "I");
+ GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_CRYPTO", "I");
gSecurityLevels.kSecurityLevelHwSecureCrypto = env->GetStaticIntField(clazz, field);
- GET_STATIC_FIELD_ID(field, clazz, "HW_SECURE_DECODE", "I");
+ GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_DECODE", "I");
gSecurityLevels.kSecurityLevelHwSecureDecode = env->GetStaticIntField(clazz, field);
- GET_STATIC_FIELD_ID(field, clazz, "HW_SECURE_ALL", "I");
+ GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_ALL", "I");
gSecurityLevels.kSecurityLevelHwSecureAll = env->GetStaticIntField(clazz, field);
jmethodID getMaxSecurityLevel;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 4cd23f9..9347674 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -50,7 +50,9 @@
}
};
private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
- .clearCapabilities().addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+ .clearCapabilities()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
private final ConnectivityManager.NetworkCallback mNetworkCallback = new ConnectivityManager
.NetworkCallback() {
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 8f80527..a128b54 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -213,6 +213,7 @@
mNetworkRequest = new NetworkRequest.Builder()
.clearCapabilities()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a444ac8..f43e719 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1724,6 +1724,9 @@
dumpSetting(s, p,
Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS,
SecureSettingsProto.Backup.LOCAL_TRANSPORT_PARAMETERS);
+ dumpSetting(s, p,
+ Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
+ SecureSettingsProto.Backup.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
p.end(backupToken);
// Settings.Secure.BLUETOOTH_ON intentionally excluded since it's deprecated.
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 68293d9..df21151 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -56,7 +56,8 @@
SystemUI-tags \
SystemUI-proto
-LOCAL_JAVA_LIBRARIES := telephony-common
+LOCAL_JAVA_LIBRARIES := telephony-common \
+ android.car
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3d49e5c..3488168 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -205,6 +205,9 @@
<!-- Listen app op changes -->
<uses-permission android:name="android.permission.WATCH_APPOPS" />
+ <!-- to read and change hvac values in a car -->
+ <uses-permission android:name="android.car.permission.ADJUST_CAR_CLIMATE" />
+
<application
android:name=".SystemUIApplication"
android:persistent="true"
diff --git a/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml b/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml
index 726d814..c0e0e59 100644
--- a/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml
@@ -14,17 +14,17 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="14dp"
- android:height="24dp"
- android:viewportWidth="14"
- android:viewportHeight="24">
+ android:width="14dp"
+ android:height="17dp"
+ android:viewportWidth="14"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M5.62,16.29H4.58V9.06l-1.79,0.8V8.88l2.67-1.17h0.16V16.29z" />
+ android:pathData="M3.77,13.48H2.55V5.05L0.46,5.98V4.84l3.12-1.36h0.19V13.48z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M11.08,11.02l1.61-3.27h1.26l-2.22,4.23l2.27,4.3h-1.27l-1.64-3.33l-1.65,3.33H8.16l2.28-4.3L8.21,7.75h1.25L11.08,11.02z" />
+ android:pathData="M10.14,7.34l1.87-3.81h1.47L10.9,8.46l2.65,5.02h-1.48l-1.91-3.88l-1.92,3.88H6.74L9.4,8.46l-2.6-4.94h1.46L10.14,7.34z" />
<path
- android:pathData="M 0 0 H 13.99 V 24 H 0 V 0 Z" />
-</vector>
\ No newline at end of file
+ android:pathData="M 0 0 H 14 V 17 H 0 V 0 Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml
index 7a539ff..e4a5bf8 100644
--- a/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml
@@ -14,17 +14,17 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="14dp"
- android:height="24dp"
- android:viewportWidth="14"
- android:viewportHeight="24">
+ android:width="14dp"
+ android:height="17dp"
+ android:viewportWidth="14"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M3.83,11.38h0.66c0.43,0,0.75-0.13,0.98-0.39s0.35-0.62,0.35-1.07c0-1-0.39-1.5-1.16-1.5c-0.37,0-0.66,0.13-0.87,0.4 S3.47,9.44,3.47,9.88H2.44c0-0.68,0.21-1.25,0.62-1.69s0.95-0.67,1.6-0.67c0.67,0,1.21,0.21,1.6,0.63s0.59,1.01,0.59,1.78 c0,0.39-0.1,0.76-0.31,1.1s-0.47,0.59-0.8,0.75c0.8,0.3,1.21,0.96,1.21,2c0,0.76-0.21,1.37-0.64,1.82s-0.98,0.68-1.66,0.68 c-0.68,0-1.22-0.21-1.64-0.64s-0.62-1-0.62-1.73h1.04c0,0.45,0.11,0.81,0.33,1.08s0.52,0.4,0.9,0.4c0.39,0,0.69-0.13,0.92-0.39 s0.34-0.66,0.34-1.2c0-1.04-0.49-1.55-1.47-1.55H3.83V11.38z" />
+ android:pathData="M1.9,7.88h0.77c0.5,0,0.88-0.15,1.15-0.46s0.4-0.72,0.4-1.25c0-1.17-0.45-1.75-1.35-1.75c-0.43,0-0.77,0.16-1.02,0.47 S1.49,5.62,1.49,6.13h-1.2c0-0.8,0.24-1.46,0.73-1.97s1.11-0.78,1.86-0.78c0.78,0,1.41,0.25,1.87,0.73S5.43,5.31,5.43,6.2 c0,0.46-0.12,0.89-0.36,1.29S4.52,8.18,4.14,8.37c0.94,0.35,1.41,1.12,1.41,2.33c0,0.89-0.25,1.6-0.74,2.12 c-0.49,0.53-1.14,0.79-1.94,0.79c-0.79,0-1.43-0.25-1.91-0.75c-0.49-0.5-0.73-1.17-0.73-2.01h1.21c0,0.53,0.13,0.95,0.38,1.26 c0.26,0.31,0.6,0.47,1.05,0.47c0.45,0,0.81-0.15,1.08-0.46s0.4-0.77,0.4-1.39c0-1.21-0.57-1.81-1.72-1.81H1.9V7.88z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M14,15.11l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.81-2.59V11.1 c0-1.2,0.24-2.09,0.72-2.69s1.19-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68s0.71,1.1,0.76,1.94h-1.07 c-0.04-0.53-0.19-0.95-0.44-1.25s-0.63-0.45-1.15-0.45c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74 c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96h-1.56v-0.92H14V15.11z" />
+ android:pathData="M13.77,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13s1.39-1.04,2.51-1.04c0.95,0,1.69,0.26,2.23,0.79s0.83,1.28,0.89,2.26h-1.25 C12.47,5.82,12.3,5.33,12,4.98s-0.74-0.52-1.34-0.52c-0.72,0-1.24,0.23-1.57,0.7S8.59,6.37,8.58,7.4v2.03c0,1,0.19,1.77,0.57,2.31 c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59H10.7V8.52h3.07V12.24z" />
<path
- android:pathData="M 0 0 H 14 V 24 H 0 V 0 Z" />
-</vector>
\ No newline at end of file
+ android:pathData="M 0 0 H 14 V 17 H 0 V 0 Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml
index b2fab0c8..e98560b 100644
--- a/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml
@@ -14,17 +14,17 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="14dp"
- android:height="24dp"
- android:viewportWidth="14"
- android:viewportHeight="24">
+ android:width="14dp"
+ android:height="17dp"
+ android:viewportWidth="14"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M6.42,13.3h0.95v0.88H6.42v1.98H5.38v-1.98H2.16v-0.64l3.17-5.91h1.09C6.42,7.63,6.42,13.3,6.42,13.3z M3.31,13.3h2.07 V9.25L3.31,13.3z" />
+ android:pathData="M5.07,10.13h1.11v1.03H5.07v2.31H3.86v-2.31H0.1v-0.75l3.7-6.9h1.27V10.13z M1.44,10.13h2.42V5.4L1.44,10.13z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M13.99,15.11l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.8-2.59V11.1 c0-1.2,0.24-2.09,0.72-2.69s1.2-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68s0.71,1.1,0.76,1.94h-1.07 c-0.04-0.53-0.19-0.95-0.44-1.25s-0.63-0.45-1.14-0.45c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74 c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96h-1.56v-0.92h2.62V15.11z" />
+ android:pathData="M13.9,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13s1.39-1.04,2.51-1.04c0.95,0,1.69,0.26,2.23,0.79s0.83,1.28,0.89,2.26h-1.25 c-0.05-0.62-0.22-1.1-0.52-1.45s-0.74-0.52-1.34-0.52c-0.72,0-1.24,0.23-1.57,0.7S8.72,6.37,8.71,7.4v2.03 c0,1,0.19,1.77,0.57,2.31c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59h-1.82V8.52h3.07V12.24z" />
<path
- android:pathData="M 0 0 H 14 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 14 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml b/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml
index bdbb2df3..bf39ea2 100644
--- a/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml
@@ -14,20 +14,20 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="20dp"
- android:height="24dp"
- android:viewportWidth="20"
- android:viewportHeight="24">
+ android:width="22dp"
+ android:height="17dp"
+ android:viewportWidth="22"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M6.18,13.3h0.95v0.88H6.18v1.98H5.14v-1.98H1.92v-0.64l3.17-5.91h1.09V13.3z M3.07,13.3h2.07V9.25L3.07,13.3z" />
+ android:pathData="M5.32,10.13h1.11v1.03H5.32v2.31H4.11v-2.31H0.35v-0.75l3.7-6.9h1.27V10.13z M1.69,10.13h2.42V5.4L1.69,10.13z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M13.75,15.11l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.8-2.59V11.1 c0-1.2,0.24-2.09,0.72-2.69s1.2-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68s0.71,1.1,0.76,1.94h-1.07 c-0.04-0.53-0.19-0.95-0.44-1.25s-0.63-0.45-1.14-0.45c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74 c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96h-1.56v-0.92h2.63V15.11z" />
+ android:pathData="M14.15,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13s1.39-1.04,2.51-1.04c0.95,0,1.69,0.26,2.23,0.79s0.83,1.28,0.89,2.26H12.9 c-0.05-0.62-0.22-1.1-0.52-1.45s-0.74-0.52-1.34-0.52c-0.72,0-1.24,0.23-1.57,0.7S8.97,6.37,8.96,7.4v2.03 c0,1,0.19,1.77,0.57,2.31c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59h-1.82V8.52h3.07V12.24z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M 20 9.64 L 18 9.64 L 18 7.64 L 17 7.64 L 17 9.64 L 15 9.64 L 15 10.64 L 17 10.64 L 17 12.64 L 18 12.64 L 18 10.64 L 20 10.64 Z" />
+ android:pathData="M 19.3 5.74 L 19.3 3.39 L 18 3.39 L 18 5.74 L 15.65 5.74 L 15.65 7.04 L 18 7.04 L 18 9.39 L 19.3 9.39 L 19.3 7.04 L 21.65 7.04 L 21.65 5.74 Z" />
<path
- android:pathData="M 0 0 H 20 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 22 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_e_mobiledata.xml b/packages/SystemUI/res/drawable/ic_e_mobiledata.xml
index 1a4a2e3..ca601d6 100644
--- a/packages/SystemUI/res/drawable/ic_e_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_e_mobiledata.xml
@@ -14,14 +14,14 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="7dp"
- android:height="24dp"
- android:viewportWidth="7"
- android:viewportHeight="24">
+ android:width="6dp"
+ android:height="17dp"
+ android:viewportWidth="6"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M6.5,12.32H3.48v3.02H7v0.92H2.41V7.73h4.53v0.92H3.48v2.75H6.5V12.32z" />
+ android:pathData="M5.1,8.88H1.57v3.53h4.1v1.07H0.32V3.52h5.28V4.6H1.57V7.8H5.1V8.88z" />
<path
- android:pathData="M 0 0 H 7 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 6 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_g_mobiledata.xml
index d6a0488..8ff6d7a 100644
--- a/packages/SystemUI/res/drawable/ic_g_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_g_mobiledata.xml
@@ -14,14 +14,14 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="8dp"
- android:height="24dp"
- android:viewportWidth="8"
- android:viewportHeight="24">
+ android:width="7dp"
+ android:height="17dp"
+ android:viewportWidth="7"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M8,15.21l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.81-2.59V11.2 c0-1.2,0.24-2.09,0.72-2.69s1.19-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68S7.95,9.39,8,10.23H6.93C6.88,9.7,6.74,9.28,6.49,8.99 S5.85,8.54,5.34,8.54c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69 c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96H5.37v-0.92H8V15.21z" />
+ android:pathData="M6.73,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13S2.5,3.39,3.62,3.39c0.95,0,1.69,0.26,2.23,0.79s0.83,1.28,0.89,2.26H5.48 c-0.05-0.62-0.22-1.1-0.52-1.45S4.22,4.46,3.62,4.46c-0.72,0-1.24,0.23-1.57,0.7S1.54,6.37,1.53,7.4v2.03c0,1,0.19,1.77,0.57,2.31 c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59H3.66V8.52h3.07V12.24z" />
<path
- android:pathData="M 0 0 H 8 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 7 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_h_mobiledata.xml b/packages/SystemUI/res/drawable/ic_h_mobiledata.xml
index be85bbb..68ea58e 100644
--- a/packages/SystemUI/res/drawable/ic_h_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_h_mobiledata.xml
@@ -14,14 +14,14 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="8dp"
- android:height="24dp"
- android:viewportWidth="8"
- android:viewportHeight="24">
+ android:width="7dp"
+ android:height="17dp"
+ android:viewportWidth="7"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M8,16.27H6.92v-3.94H3.49v3.94H2.42V7.73h1.07v3.67h3.43V7.73H8V16.27z" />
+ android:pathData="M6.76,13.48H5.5v-4.6H1.49v4.6H0.24V3.52h1.25V7.8H5.5V3.52h1.26V13.48z" />
<path
- android:pathData="M 0 0 H 8 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 7 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml b/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml
index f31f83c..42128002 100644
--- a/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml
@@ -14,17 +14,17 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="14dp"
- android:height="24dp"
- android:viewportWidth="14"
- android:viewportHeight="24">
+ android:width="15dp"
+ android:height="17dp"
+ android:viewportWidth="15"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M7.64,16.27H6.56v-3.94H3.13v3.94H2.06V7.73h1.07v3.67h3.43V7.73h1.08V16.27z" />
+ android:pathData="M7.01,13.48H5.75v-4.6H1.74v4.6H0.49V3.52h1.25V7.8h4.01V3.52h1.26V13.48z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M 14 9.73 L 12 9.73 L 12 7.73 L 11 7.73 L 11 9.73 L 9 9.73 L 9 10.73 L 11 10.73 L 11 12.73 L 12 12.73 L 12 10.73 L 14 10.73 Z" />
+ android:pathData="M 12.16 5.74 L 12.16 3.39 L 10.86 3.39 L 10.86 5.74 L 8.51 5.74 L 8.51 7.04 L 10.86 7.04 L 10.86 9.39 L 12.16 9.39 L 12.16 7.04 L 14.51 7.04 L 14.51 5.74 Z" />
<path
- android:pathData="M 0 0 H 14 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 15 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml b/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml
index e45b5e0..7536f51 100644
--- a/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml
@@ -14,20 +14,20 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="18dp"
- android:height="24dp"
- android:viewportWidth="18"
- android:viewportHeight="24">
+ android:width="18dp"
+ android:height="17dp"
+ android:viewportWidth="18"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M3.79,15.35h3.35v0.92H2.71V7.73h1.08V15.35z" />
+ android:pathData="M1.34,12.4h3.9v1.07H0.08V3.52h1.26V12.4z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M12.15,8.65H9.91v7.61H8.84V8.65H6.6V7.73h5.55V8.65z" />
+ android:pathData="M11.1,4.6H8.48v8.88H7.23V4.6H4.62V3.52h6.48V4.6z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M17.5,12.32h-3.02v3.02H18v0.92h-4.59V7.73h4.53v0.92h-3.46v2.75h3.02V12.32z" />
+ android:pathData="M17.34,8.88h-3.52v3.53h4.1v1.07h-5.35V3.52h5.28V4.6h-4.03V7.8h3.52V8.88z" />
<path
- android:pathData="M 0 0 H 18 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 18 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml b/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml
index 553a5bd..302e3bd 100644
--- a/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml
@@ -14,23 +14,23 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
+ android:width="26dp"
+ android:height="17dp"
+ android:viewportWidth="26"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M3.91,15.35h3.35v0.92H2.84V7.73h1.08V15.35z" />
+ android:pathData="M1.59,12.4h3.9v1.07H0.33V3.52h1.26V12.4z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M12.28,8.65h-2.24v7.61H8.96V8.65H6.73V7.73h5.55V8.65z" />
+ android:pathData="M11.35,4.6H8.73v8.88H7.48V4.6H4.87V3.52h6.48V4.6z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M17.63,12.32h-3.02v3.02h3.52v0.92h-4.59V7.73h4.53v0.92h-3.46v2.75h3.02V12.32z" />
+ android:pathData="M17.59,8.88h-3.52v3.53h4.1v1.07h-5.35V3.52h5.28V4.6h-4.03V7.8h3.52V8.88z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M 24 9.76 L 22 9.76 L 22 7.76 L 21 7.76 L 21 9.76 L 19 9.76 L 19 10.76 L 21 10.76 L 21 12.76 L 22 12.76 L 22 10.76 L 24 10.76 Z" />
+ android:pathData="M 23.32 5.74 L 23.32 3.39 L 22.02 3.39 L 22.02 5.74 L 19.67 5.74 L 19.67 7.04 L 22.02 7.04 L 22.02 9.39 L 23.32 9.39 L 23.32 7.04 L 25.67 7.04 L 25.67 5.74 Z" />
<path
- android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 26 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/qs_header_status_dot.xml b/packages/SystemUI/res/drawable/qs_header_status_dot.xml
deleted file mode 100644
index 69bfd49..0000000
--- a/packages/SystemUI/res/drawable/qs_header_status_dot.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
- <solid android:color="@android:color/white"/>
-</shape>
diff --git a/packages/SystemUI/res/drawable/stat_sys_roaming.xml b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
index bd2edf3..0dd9f5a 100644
--- a/packages/SystemUI/res/drawable/stat_sys_roaming.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
@@ -1,28 +1,27 @@
<!--
-Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2018 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
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- 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.
+ 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/signal_icon_size"
android:height="@dimen/signal_icon_size"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M7.8,7.2L9,10H7L5.87,7.33H4V10H2V2h5c1.13,0,2,0.87,2,2v1.33C9,6.13,8.47,6.87,7.8,7.2z M7,4H4v1.33h3V4z" />
- <path
- android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
- <path
- android:pathData="M0,0h24v24H0V0z" />
-</vector>
\ No newline at end of file
+ android:viewportWidth="17"
+ android:viewportHeight="17">
+
+<path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M2.93,4.81H1.81V7.4H1V1h1.79c0.63,0,1.1,0.16,1.42,0.49S4.7,2.29,4.7,2.92c0,0.4-0.09,0.74-0.26,1.04 C4.26,4.25,4.02,4.48,3.7,4.63l1.24,2.72V7.4H4.07L2.93,4.81z M1.81,4.12h0.98c0.34,0,0.61-0.11,0.81-0.33 c0.2-0.22,0.3-0.51,0.3-0.87c0-0.82-0.37-1.23-1.12-1.23H1.81V4.12z" />
+<path
+ android:pathData="M 0 0 H 17 V 17 H 0 V 0 Z" />
+</vector>
diff --git a/packages/SystemUI/res/layout/car_facet_button.xml b/packages/SystemUI/res/layout/car_facet_button.xml
index f432d36..ad86049 100644
--- a/packages/SystemUI/res/layout/car_facet_button.xml
+++ b/packages/SystemUI/res/layout/car_facet_button.xml
@@ -42,6 +42,7 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:animateLayoutChanges="true"
+ android:src="@drawable/car_ic_arrow"
android:background="@android:color/transparent"
android:scaleType="fitCenter">
</com.android.keyguard.AlphaOptimizedImageButton>
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
new file mode 100644
index 0000000..a65ff16
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, 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.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:background="@drawable/system_bar_background">
+
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:id="@+id/nav_buttons"
+ android:orientation="vertical"
+ android:gravity="top"
+ android:paddingTop="30dp"
+ android:layout_weight="1"
+ android:background="@drawable/system_bar_background"
+ android:animateLayoutChanges="true">
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/home"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+ android:src="@drawable/car_ic_overview"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingTop="30dp"
+ android:paddingBottom="30dp"
+ />
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/hvac"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+ systemui:broadcast="true"
+ android:src="@drawable/car_ic_hvac"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingTop="30dp"
+ android:paddingBottom="30dp"
+ />
+ </LinearLayout>
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
new file mode 100644
index 0000000..b0488ae
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, 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.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:background="@drawable/system_bar_background">
+
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:orientation="horizontal"
+ android:id="@+id/nav_buttons"
+ android:gravity="left"
+ android:paddingLeft="30dp"
+ android:layout_weight="1"
+ android:animateLayoutChanges="true">
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/home"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+ android:src="@drawable/car_ic_overview"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingLeft="30dp"
+ android:paddingRight="30dp"
+ />
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/hvac"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+ systemui:broadcast="true"
+ android:src="@drawable/car_ic_hvac"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingLeft="30dp"
+ android:paddingRight="30dp"
+ />
+ </LinearLayout>
+</com.android.systemui.statusbar.car.CarNavigationBarView>
+
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
new file mode 100644
index 0000000..a65ff16
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, 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.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:background="@drawable/system_bar_background">
+
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:id="@+id/nav_buttons"
+ android:orientation="vertical"
+ android:gravity="top"
+ android:paddingTop="30dp"
+ android:layout_weight="1"
+ android:background="@drawable/system_bar_background"
+ android:animateLayoutChanges="true">
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/home"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+ android:src="@drawable/car_ic_overview"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingTop="30dp"
+ android:paddingBottom="30dp"
+ />
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/hvac"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+ systemui:broadcast="true"
+ android:src="@drawable/car_ic_hvac"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingTop="30dp"
+ android:paddingBottom="30dp"
+ />
+ </LinearLayout>
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/car_status_bar_header.xml b/packages/SystemUI/res/layout/car_status_bar_header.xml
index 158907e..f2ef301 100644
--- a/packages/SystemUI/res/layout/car_status_bar_header.xml
+++ b/packages/SystemUI/res/layout/car_status_bar_header.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<!-- Extends RelativeLayout -->
+<!-- Extends LinearLayout -->
<com.android.systemui.qs.car.CarStatusBarHeader
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
@@ -23,22 +23,21 @@
android:paddingStart="8dp"
android:paddingEnd="8dp" >
- <include
- layout="@layout/system_icons"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true" />
+ <include layout="@layout/system_icons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical|end"
+ android:layout_weight="1"
+ />
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
android:singleLine="true"
android:paddingStart="@dimen/status_bar_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_clock_end_padding"
- systemui:showDark="false" />
+ android:gravity="center_vertical|end"
+ />
</com.android.systemui.qs.car.CarStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/car_top_navigation_bar.xml b/packages/SystemUI/res/layout/car_top_navigation_bar.xml
new file mode 100644
index 0000000..e16014b
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_top_navigation_bar.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+** Copyright 2018, 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.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:background="@drawable/system_bar_background">
+
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:singleLine="true"
+ android:paddingStart="@dimen/status_bar_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_clock_end_padding"
+ android:gravity="center_vertical"
+ />
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
+
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index cc6e3bf..5ecd380 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -50,7 +50,8 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
- android:paddingEnd="1dp"
+ android:paddingStart="1dp"
+ android:paddingEnd="2dp"
android:visibility="gone" />
<Space
android:id="@+id/mobile_roaming_space"
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
index 03e8451..54baa4a 100644
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -54,15 +54,14 @@
android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
android:textAppearance="@style/TextAppearance.QS.TileLabel" />
- <ImageView
+ <View
android:id="@+id/status_separator"
- android:layout_width="2dp"
- android:layout_height="2dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:layout_gravity="center_vertical"
- android:src="@drawable/qs_header_status_dot"
- android:tint="?android:attr/textColorPrimary" />
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="10dp"
+ android:background="@android:color/white"
+ android:backgroundTint="?android:attr/textColorPrimary" />
<ImageView
android:id="@+id/ringer_mode_icon"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 2e7ab7f..f15ca9e 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -55,19 +55,6 @@
android:paddingStart="8dp"
/>
- <!-- TODO: remove -->
- <ImageButton
- android:id="@+id/helper"
- android:layout_width="48dp"
- android:layout_height="@*android:dimen/notification_header_height"
- android:layout_gravity="top|end"
- android:layout_marginEnd="6dp"
- android:src="@drawable/ic_dnd"
- android:tint="#FF0000"
- android:background="@drawable/ripple_drawable"
- android:visibility="visible"
- />
-
<ViewStub
android:layout="@layout/notification_children_container"
android:id="@+id/child_container_stub"
diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml
index b1097c3..5e4bd79 100644
--- a/packages/SystemUI/res/values/attrs_car.xml
+++ b/packages/SystemUI/res/values/attrs_car.xml
@@ -27,6 +27,13 @@
<attr name="categories" format="string"/>
<!-- package names that will be added as extras to the fired intents -->
<attr name="packages" format="string" />
+ <!-- Alpha value to used when in selected state. Defaults 1f -->
+ <attr name="selectedAlpha" format="float" />
+ <!-- Alpha value to used when in un-selected state. Defaults 0.7f -->
+ <attr name="unselectedAlpha" format="float" />
+ <!-- Render a "more" icon. Defaults true -->
+ <attr name="useMoreIcon" format="boolean" />
+
</declare-styleable>
@@ -39,4 +46,11 @@
<!-- start the intent as a broad cast instead of an activity if true-->
<attr name="broadcast" format="boolean"/>
</declare-styleable>
+
+ <!-- Custom attributes to configure hvac values -->
+ <declare-styleable name="TemperatureView">
+ <attr name="hvacAreaId" format="integer"/>
+ <attr name="hvacPropertyId" format="integer"/>
+ <attr name="hvacTempFormat" format="string"/>
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 816c598..8cff56d 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -228,9 +228,14 @@
mHandler.removeCallbacks(mConnectionRunnable);
Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
.setPackage(mRecentsComponentName.getPackageName());
- boolean bound = mContext.bindServiceAsUser(launcherServiceIntent,
- mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
- UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
+ boolean bound = false;
+ try {
+ bound = mContext.bindServiceAsUser(launcherServiceIntent,
+ mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
+ UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
+ } catch (SecurityException e) {
+ Log.e(TAG_OPS, "Unable to bind because of security error", e);
+ }
if (!bound) {
// Retry after exponential backoff timeout
final long timeoutMs = (long) Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts);
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
index 245d240..9459ce1 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
@@ -21,6 +21,8 @@
import com.android.systemui.Dependency.DependencyProvider;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.statusbar.NotificationEntryManager;
+import com.android.systemui.statusbar.car.CarFacetButtonController;
+import com.android.systemui.statusbar.car.hvac.HvacController;
/**
* Class factory to provide car specific SystemUI components.
@@ -32,5 +34,7 @@
super.injectDependencies(providers, context);
providers.put(NotificationEntryManager.class,
() -> new CarNotificationEntryManager(context));
+ providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
+ providers.put(HvacController.class, () -> new HvacController(context));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 3ba3d0e..5c0576d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -284,14 +284,19 @@
// Defer the update of the current movement bounds until after the user finishes
// touching the screen
} else {
- final Rect toMovementBounds = mMenuState == MENU_STATE_FULL
- ? expandedMovementBounds
- : normalMovementBounds;
- animateToOffset(animatingBounds, toMovementBounds,
- fromImeAdjustment,
- fromImeAdjustment ? mIsImeShowing : mIsShelfShowing,
- // Shelf height serves as an offset, but does not change movement bounds.
- fromImeAdjustment ? mImeOffset : mShelfHeight);
+ final int adjustedOffset = Math.max(mIsImeShowing ? mImeHeight + mImeOffset : 0,
+ mIsShelfShowing ? mShelfHeight : 0);
+ Rect normalAdjustedBounds = new Rect();
+ mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalAdjustedBounds,
+ adjustedOffset);
+ Rect expandedAdjustedBounds = new Rect();
+ mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds,
+ expandedAdjustedBounds, adjustedOffset);
+ final Rect toAdjustedBounds = mMenuState == MENU_STATE_FULL
+ ? expandedAdjustedBounds
+ : normalAdjustedBounds;
+
+ animateToOffset(animatingBounds, toAdjustedBounds);
}
}
@@ -313,23 +318,13 @@
}
}
- private void animateToOffset(Rect animatingBounds, Rect toMovementBounds,
- boolean fromImeAdjustment, boolean showing, int offset) {
+ private void animateToOffset(Rect animatingBounds, Rect toAdjustedBounds) {
final Rect bounds = new Rect(animatingBounds);
- if (showing) {
- // IME/shelf visible, apply the IME/shelf offset if the space allows for it
- final int calculatedOffset = toMovementBounds.bottom - Math.max(toMovementBounds.top,
- toMovementBounds.bottom - offset);
- bounds.offset(0,
- Math.min(0, toMovementBounds.bottom - calculatedOffset - bounds.top));
- } else {
- // IME/shelf hidden
- if (bounds.top >= (mMovementBounds.bottom - offset)) {
- bounds.offset(0, toMovementBounds.bottom - bounds.top -
- // Counter going back home from search where keyboard is up.
- (fromImeAdjustment ? mShelfHeight : 0));
- }
+ if (toAdjustedBounds.bottom < mMovementBounds.bottom
+ && bounds.top < toAdjustedBounds.bottom) {
+ return;
}
+ bounds.offset(0, toAdjustedBounds.bottom - bounds.top);
mMotionHelper.animateToOffset(bounds);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
index 6797bb9..ec18376 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
@@ -19,7 +19,7 @@
import android.support.annotation.IdRes;
import android.util.AttributeSet;
import android.view.View;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
@@ -30,7 +30,7 @@
* A view that forms the header of the notification panel. This view will ensure that any
* status icons that are displayed are tinted accordingly to the current theme.
*/
-public class CarStatusBarHeader extends RelativeLayout {
+public class CarStatusBarHeader extends LinearLayout {
public CarStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 05a5a8e..03b263d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -183,7 +183,6 @@
private AboveShelfChangedListener mAboveShelfChangedListener;
private HeadsUpManager mHeadsUpManager;
private Consumer<Boolean> mHeadsUpAnimatingAwayListener;
- private View mHelperButton;
private boolean mChildIsExpanding;
private boolean mJustClicked;
@@ -401,8 +400,6 @@
updateLimits();
updateIconVisibilities();
updateShelfIconColor();
-
- showBlockingHelperButton(mEntry.userSentiment == USER_SENTIMENT_NEGATIVE);
updateRippleAllowed();
}
@@ -1426,10 +1423,6 @@
requestLayout();
}
- public void showBlockingHelperButton(boolean show) {
- mHelperButton.setVisibility(show ? View.VISIBLE : View.GONE);
- }
-
public void showAppOpsIcons(ArraySet<Integer> activeOps) {
if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) {
mChildrenContainer.getHeaderView().showAppOpsIcons(activeOps);
@@ -1459,11 +1452,6 @@
mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
- mHelperButton = findViewById(R.id.helper);
- mHelperButton.setOnClickListener(view -> {
- doLongClickCallback();
- });
-
for (NotificationContentView l : mLayouts) {
l.setExpandClickListener(mExpandClickListener);
l.setContainingNotification(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 775faee..4b6ab64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -18,25 +18,21 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import android.Manifest;
import android.app.AppGlobals;
-import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
+import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.Context;
import android.graphics.drawable.Icon;
-import android.os.AsyncTask;
-import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.SnoozeCriterion;
@@ -46,10 +42,8 @@
import android.view.View;
import android.widget.ImageView;
import android.widget.RemoteViews;
-import android.Manifest;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.NotificationColorUtil;
import com.android.systemui.Dependency;
@@ -454,47 +448,44 @@
return Ranking.VISIBILITY_NO_OVERRIDE;
}
- public boolean shouldSuppressFullScreenIntent(String key) {
+ public boolean shouldSuppressFullScreenIntent(StatusBarNotification sbn) {
+ return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
+ }
+
+ public boolean shouldSuppressPeek(StatusBarNotification sbn) {
+ return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_PEEK);
+ }
+
+ public boolean shouldSuppressStatusBar(StatusBarNotification sbn) {
+ return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_STATUS_BAR);
+ }
+
+ public boolean shouldSuppressAmbient(StatusBarNotification sbn) {
+ return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_AMBIENT);
+ }
+
+ public boolean shouldSuppressNotificationList(StatusBarNotification sbn) {
+ return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+ }
+
+ private boolean shouldSuppressVisualEffect(StatusBarNotification sbn, int effect) {
+ if (isExemptFromDndVisualSuppression(sbn)) {
+ return false;
+ }
+ String key = sbn.getKey();
if (mRankingMap != null) {
getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects()
- & SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) != 0;
+ return (mTmpRanking.getSuppressedVisualEffects() & effect) != 0;
}
return false;
}
- public boolean shouldSuppressPeek(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects()
- & SUPPRESSED_EFFECT_PEEK) != 0;
+ protected boolean isExemptFromDndVisualSuppression(StatusBarNotification sbn) {
+ if ((sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ return true;
}
- return false;
- }
-
- public boolean shouldSuppressStatusBar(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects()
- & SUPPRESSED_EFFECT_STATUS_BAR) != 0;
- }
- return false;
- }
-
- public boolean shouldSuppressAmbient(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects()
- & SUPPRESSED_EFFECT_AMBIENT) != 0;
- }
- return false;
- }
-
- public boolean shouldSuppressNotificationList(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects()
- & SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
+ if (sbn.getNotification().isMediaNotification()) {
+ return true;
}
return false;
}
@@ -620,11 +611,11 @@
return true;
}
- if (mEnvironment.isDozing() && shouldSuppressAmbient(sbn.getKey())) {
+ if (mEnvironment.isDozing() && shouldSuppressAmbient(sbn)) {
return true;
}
- if (!mEnvironment.isDozing() && shouldSuppressNotificationList(sbn.getKey())) {
+ if (!mEnvironment.isDozing() && shouldSuppressNotificationList(sbn)) {
return true;
}
@@ -641,9 +632,14 @@
// this is a foreground-service disclosure for a user that does not need to show one
return true;
}
- if (mFsc.isSystemAlertNotification(sbn) && !mFsc.isSystemAlertWarningNeeded(
- sbn.getUserId(), sbn.getPackageName())) {
- return true;
+ if (mFsc.isSystemAlertNotification(sbn)) {
+ final String[] apps = sbn.getNotification().extras.getStringArray(
+ Notification.EXTRA_FOREGROUND_APPS);
+ if (apps != null && apps.length >= 1) {
+ if (!mFsc.isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
+ return true;
+ }
+ }
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 7a7cc99..45df450 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -299,12 +299,12 @@
updateNotifications();
}
- private boolean shouldSuppressFullScreenIntent(String key) {
+ private boolean shouldSuppressFullScreenIntent(StatusBarNotification sbn) {
if (mPresenter.isDeviceInVrMode()) {
return true;
}
- return mNotificationData.shouldSuppressFullScreenIntent(key);
+ return mNotificationData.shouldSuppressFullScreenIntent(sbn);
}
private void inflateViews(NotificationData.Entry entry, ViewGroup parent) {
@@ -690,7 +690,7 @@
NotificationData.Entry shadeEntry = createNotificationViews(notification);
boolean isHeadsUped = shouldPeek(shadeEntry);
if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
- if (shouldSuppressFullScreenIntent(key)) {
+ if (shouldSuppressFullScreenIntent(notification)) {
if (DEBUG) {
Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
}
@@ -846,13 +846,13 @@
return false;
}
- if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(sbn.getKey())) {
+ if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(sbn)) {
if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
return false;
}
// Peeking triggers an ambient display pulse, so disable peek is ambient is active
- if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(sbn.getKey())) {
+ if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(sbn)) {
if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index a2f336e..82ad74e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -165,6 +165,7 @@
mIsForeground =
(mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
mIsForBlockingHelper = isForBlockingHelper;
+ mAppUid = mSbn.getUid();
int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
pkg, mAppUid, false /* includeDeleted */);
@@ -173,9 +174,9 @@
} else {
// Special behavior for the Default channel if no other channels have been defined.
mIsSingleDefaultChannel = mNumNotificationChannels == 1
- && mSingleNotificationChannel.getId()
- .equals(NotificationChannel.DEFAULT_CHANNEL_ID)
- && numTotalChannels <= 1;
+ && mSingleNotificationChannel.getId().equals(
+ NotificationChannel.DEFAULT_CHANNEL_ID)
+ && numTotalChannels == 1;
}
try {
@@ -210,7 +211,6 @@
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE);
if (info != null) {
- mAppUid = mSbn.getUid();
mAppName = String.valueOf(mPm.getApplicationLabel(info));
pkgicon = mPm.getApplicationIcon(info);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index b1e08b8..fd3a9d5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -350,9 +350,6 @@
}
}
- row.showBlockingHelperButton(entry.userSentiment ==
- NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
-
row.showAppOpsIcons(entry.mActiveAppOps);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
index 53101a5..5f3e2e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -3,6 +3,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
@@ -10,6 +11,7 @@
import android.widget.LinearLayout;
import com.android.keyguard.AlphaOptimizedImageButton;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
@@ -21,9 +23,6 @@
* other music apps installed.
*/
public class CarFacetButton extends LinearLayout {
- private static final float SELECTED_ALPHA = 1f;
- private static final float UNSELECTED_ALPHA = 0.7f;
-
private static final String FACET_FILTER_DELIMITER = ";";
/**
* Extra information to be sent to a helper to make the decision of what app to launch when
@@ -42,6 +41,10 @@
private String[] mFacetCategories;
/** App packages that are allowed to be used with this widget */
private String[] mFacetPackages;
+ private int mIconResourceId;
+ private boolean mUseMoreIcon = true;
+ private float mSelectedAlpha = 1f;
+ private float mUnselectedAlpha = 1f;
public CarFacetButton(Context context, AttributeSet attrs) {
@@ -53,6 +56,10 @@
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
setupIntents(typedArray);
setupIcons(typedArray);
+ CarFacetButtonController carFacetButtonController = Dependency.get(
+ CarFacetButtonController.class);
+ carFacetButtonController.addFacetButton(this);
+
}
/**
@@ -96,21 +103,25 @@
private void setupIcons(TypedArray styledAttributes) {
+ mSelectedAlpha = styledAttributes.getFloat(
+ R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
+ mUnselectedAlpha = styledAttributes.getFloat(
+ R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha);
mIcon = findViewById(R.id.car_nav_button_icon);
mIcon.setScaleType(ImageView.ScaleType.CENTER);
mIcon.setClickable(false);
- mIcon.setAlpha(UNSELECTED_ALPHA);
- int iconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
- if (iconResourceId == 0) {
+ mIcon.setAlpha(mUnselectedAlpha);
+ mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
+ if (mIconResourceId == 0) {
throw new RuntimeException("specified icon resource was not found and is required");
}
- mIcon.setImageResource(iconResourceId);
+ mIcon.setImageResource(mIconResourceId);
mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
mMoreIcon.setClickable(false);
- mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow));
- mMoreIcon.setAlpha(UNSELECTED_ALPHA);
+ mMoreIcon.setAlpha(mSelectedAlpha);
mMoreIcon.setVisibility(GONE);
+ mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true);
}
/**
@@ -145,17 +156,27 @@
/**
* Updates the visual state to let the user know if it's been selected.
* @param selected true if should update the alpha of the icon to selected, false otherwise
- * @param showMoreIcon true if the "more icon" should be shown, false otherwise
+ * @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this
+ * is ignored if the attribute useMoreIcon is set to false
*/
public void setSelected(boolean selected, boolean showMoreIcon) {
mSelected = selected;
if (selected) {
- mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
- mMoreIcon.setAlpha(SELECTED_ALPHA);
- mIcon.setAlpha(SELECTED_ALPHA);
+ if (mUseMoreIcon) {
+ mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
+ }
+ mIcon.setAlpha(mSelectedAlpha);
} else {
mMoreIcon.setVisibility(GONE);
- mIcon.setAlpha(UNSELECTED_ALPHA);
+ mIcon.setAlpha(mUnselectedAlpha);
+ }
+ }
+
+ public void setIcon(Drawable d) {
+ if (d != null) {
+ mIcon.setImageDrawable(d);
+ } else {
+ mIcon.setImageResource(mIconResourceId);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index e8c9a5e..2841136 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -5,8 +5,6 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.view.View;
-import android.view.ViewGroup;
import java.util.HashMap;
import java.util.List;
@@ -29,39 +27,25 @@
}
/**
- * Goes through the supplied CarNavigationBarView and keeps track of all the CarFacetButtons
- * such that it can select and unselect them based on running task chages
- * @param bar that may contain CarFacetButtons
+ * Add facet button to this controller. The expected use is for the facet button
+ * to get a reference to this controller via {@link com.android.systemui.Dependency}
+ * and self add.
+ * @param facetButton
*/
- public void addCarNavigationBar(CarNavigationBarView bar) {
- findFacets(bar);
- }
+ public void addFacetButton(CarFacetButton facetButton) {
+ String[] categories = facetButton.getCategories();
+ for (int j = 0; j < categories.length; j++) {
+ String category = categories[j];
+ mButtonsByCategory.put(category, facetButton);
+ }
- private void findFacets(ViewGroup root) {
- final int childCount = root.getChildCount();
-
- for (int i = 0; i < childCount; ++i) {
- final View v = root.getChildAt(i);
- if (v instanceof CarFacetButton) {
- CarFacetButton facetButton = (CarFacetButton) v;
- String[] categories = facetButton.getCategories();
- for (int j = 0; j < categories.length; j++) {
- String category = categories[j];
- mButtonsByCategory.put(category, facetButton);
- }
-
- String[] facetPackages = facetButton.getFacetPackages();
- for (int j = 0; j < facetPackages.length; j++) {
- String facetPackage = facetPackages[j];
- mButtonsByPackage.put(facetPackage, facetButton);
- }
- } else if (v instanceof ViewGroup) {
- findFacets((ViewGroup) v);
- }
+ String[] facetPackages = facetButton.getFacetPackages();
+ for (int j = 0; j < facetPackages.length; j++) {
+ String facetPackage = facetPackages[j];
+ mButtonsByPackage.put(facetPackage, facetButton);
}
}
-
/**
* This will unselect the currently selected CarFacetButton and determine which one should be
* selected next. It does this by reading the properties on the CarFacetButton and seeing if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
index 1d9ef61..e73b173 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -22,6 +22,7 @@
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
+import android.widget.TextView;
import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.R;
@@ -36,9 +37,11 @@
private LinearLayout mNavButtons;
private AlphaOptimizedImageButton mNotificationsButton;
private CarStatusBar mCarStatusBar;
+ private Context mContext;
public CarNavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mContext = context;
}
@Override
@@ -46,7 +49,9 @@
mNavButtons = findViewById(R.id.nav_buttons);
mNotificationsButton = findViewById(R.id.notifications);
- mNotificationsButton.setOnClickListener(this::onNotificationsClick);
+ if (mNotificationsButton != null) {
+ mNotificationsButton.setOnClickListener(this::onNotificationsClick);
+ }
}
void setStatusBar(CarStatusBar carStatusBar) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index c15a013..a95d0a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -17,13 +17,9 @@
package com.android.systemui.statusbar.car;
import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.UserHandle;
+import android.os.SystemProperties;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
@@ -45,8 +41,8 @@
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.car.hvac.HvacController;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.NavigationBarView;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -60,6 +56,8 @@
public class CarStatusBar extends StatusBar implements
CarBatteryController.BatteryViewHandler {
private static final String TAG = "CarStatusBar";
+ public static final boolean ENABLE_HVAC_CONNECTION
+ = !SystemProperties.getBoolean("android.car.hvac.demo", true);
private TaskStackListenerImpl mTaskStackListener;
@@ -93,6 +91,11 @@
createBatteryController();
mCarBatteryController.startListening();
+
+ if (ENABLE_HVAC_CONNECTION) {
+ Log.d(TAG, "Connecting to HVAC service");
+ Dependency.get(HvacController.class).connectToCarService();
+ }
}
@Override
@@ -164,7 +167,7 @@
@Override
protected void createNavigationBar() {
- mCarFacetButtonController = new CarFacetButtonController(mContext);
+ mCarFacetButtonController = Dependency.get(CarFacetButtonController.class);
if (mNavigationBarView != null) {
return;
}
@@ -225,7 +228,6 @@
lp.windowAnimations = 0;
- mCarFacetButtonController.addCarNavigationBar(mNavigationBarView);
mWindowManager.addView(mNavigationBarWindow, lp);
}
@@ -243,7 +245,6 @@
throw new RuntimeException("Unable to build left nav bar due to missing layout");
}
mLeftNavigationBarView.setStatusBar(this);
- mCarFacetButtonController.addCarNavigationBar(mLeftNavigationBarView);
WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
widthForSides, LayoutParams.MATCH_PARENT,
@@ -275,7 +276,6 @@
throw new RuntimeException("Unable to build right nav bar due to missing layout");
}
mRightNavigationBarView.setStatusBar(this);
- mCarFacetButtonController.addCarNavigationBar(mRightNavigationBarView);
WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
widthForSides, LayoutParams.MATCH_PARENT,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
new file mode 100644
index 0000000..23bf887
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.car.hvac;
+
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.hvac.CarHvacManager;
+import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Manages the connection to the Car service and delegates value changes to the registered
+ * {@link TemperatureView}s
+ */
+public class HvacController {
+
+ public static final String TAG = "HvacController";
+ public final static int BIND_TO_HVAC_RETRY_DELAY = 5000;
+
+ private Context mContext;
+ private Handler mHandler;
+ private Car mCar;
+ private CarHvacManager mHvacManager;
+ private HashMap<HvacKey, TemperatureView> mTempComponents = new HashMap<>();
+
+ public HvacController(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Create connection to the Car service. Note: call backs from the Car service
+ * ({@link CarHvacManager}) will happen on the same thread this method was called from.
+ */
+ public void connectToCarService() {
+ mHandler = new Handler();
+ mCar = Car.createCar(mContext, mServiceConnection, mHandler);
+ if (mCar != null) {
+ // note: this connect call handles the retries
+ mCar.connect();
+ }
+ }
+
+ /**
+ * Registers callbacks and initializes components upon connection.
+ */
+ private ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ try {
+ service.linkToDeath(mRestart, 0);
+ mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
+ mHvacManager.registerCallback(mHardwareCallback);
+ initComponents();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to correctly connect to HVAC", e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ destroyHvacManager();
+ }
+ };
+
+ private void destroyHvacManager() {
+ if (mHvacManager != null) {
+ mHvacManager.unregisterCallback(mHardwareCallback);
+ mHvacManager = null;
+ }
+ }
+
+ /**
+ * If the connection to car service goes away then restart it.
+ */
+ private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ Log.d(TAG, "Death of HVAC triggering a restart");
+ if (mCar != null) {
+ mCar.disconnect();
+ }
+ destroyHvacManager();
+ mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
+ }
+ };
+
+ /**
+ * Add component to list and initialize it if the connection is up.
+ * @param temperatureView
+ */
+ public void addHvacTextView(TemperatureView temperatureView) {
+ mTempComponents.put(
+ new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId()),
+ temperatureView);
+ initComponent(temperatureView);
+ }
+
+ private void initComponents() {
+ Iterator<Map.Entry<HvacKey, TemperatureView>> iterator =
+ mTempComponents.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<HvacKey, TemperatureView> next = iterator.next();
+ initComponent(next.getValue());
+ }
+ }
+
+
+ private void initComponent(TemperatureView view) {
+ int id = view.getPropertyId();
+ int zone = view.getAreaId();
+ try {
+ if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
+ view.setTemp(Float.NaN);
+ return;
+ }
+ view.setTemp(mHvacManager.getFloatProperty(id, zone));
+ } catch (CarNotConnectedException e) {
+ view.setTemp(Float.NaN);
+ Log.e(TAG, "Failed to get value from hvac service", e);
+ }
+ }
+
+ /**
+ * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
+ * match.
+ */
+ private final CarHvacEventCallback mHardwareCallback = new CarHvacEventCallback() {
+ @Override
+ public void onChangeEvent(final CarPropertyValue val) {
+ try {
+ int areaId = val.getAreaId();
+ int propertyId = val.getPropertyId();
+ TemperatureView temperatureView = mTempComponents.get(
+ new HvacKey(propertyId, areaId));
+ if (temperatureView != null) {
+ float value = (float) val.getValue();
+ temperatureView.setTemp(value);
+ } // else the data is not of interest
+ } catch (Exception e) {
+ // catch all so we don't take down the sysui if a new data type is
+ // introduced.
+ Log.e(TAG, "Failed handling hvac change event", e);
+ }
+ }
+
+ @Override
+ public void onErrorEvent(final int propertyId, final int zone) {
+ Log.d(TAG, "HVAC error event, propertyId: " + propertyId +
+ " zone: " + zone);
+ }
+ };
+
+ /**
+ * Key for storing {@link TemperatureView}s in a hash map
+ */
+ private static class HvacKey {
+
+ int mPropertyId;
+ int mAreaId;
+
+ public HvacKey(int propertyId, int areaId) {
+ mPropertyId = propertyId;
+ mAreaId = areaId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ HvacKey hvacKey = (HvacKey) o;
+ return mPropertyId == hvacKey.mPropertyId &&
+ mAreaId == hvacKey.mAreaId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPropertyId, mAreaId);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
new file mode 100644
index 0000000..4049ec3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.car.hvac;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+
+/**
+ * Simple text display of HVAC properties, It is designed to show temperature and is configured in
+ * the XML.
+ * XML properties:
+ * hvacPropertyId - Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
+ * hvacAreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
+ * hvacTempFormat - Example: "%.1f\u00B0" (1 decimal and the degree symbol)
+ *
+ * Note: It registers itself with {@link HvacController}
+ */
+public class TemperatureView extends TextView {
+
+ private final int mAreaId;
+ private final int mPropertyId;
+ private final String mTempFormat;
+
+ public TemperatureView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TemperatureView);
+ mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId,-1);
+ mPropertyId = typedArray.getInt(R.styleable.TemperatureView_hvacPropertyId, -1);
+ String format = typedArray.getString(R.styleable.TemperatureView_hvacTempFormat);
+ mTempFormat = (format == null) ? "%.1f\u00B0" : format;
+
+ // register with controller
+ HvacController hvacController = Dependency.get(HvacController.class);
+ hvacController.addHvacTextView(this);
+ }
+
+ /**
+ * Formats the float for display
+ * @param temp - The current temp or NaN
+ */
+ public void setTemp(float temp) {
+ if (Float.isNaN(temp)) {
+ setText("--");
+ return;
+ }
+ setText(String.format(mTempFormat, temp));
+ }
+
+ /**
+ * @return propertiyId Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
+ */
+ public int getPropertyId() {
+ return mPropertyId;
+ }
+
+ /**
+ * @return hvac AreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
+ */
+ public int getAreaId() {
+ return mAreaId;
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index fcd4e8f..df2b817 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -334,6 +334,20 @@
return mKeyguardView != null && mKeyguardView.hasDismissActions();
}
+ public int getTop() {
+ if (mKeyguardView == null) {
+ return 0;
+ }
+
+ int top = mKeyguardView.getTop();
+ // The password view has an extra top padding that should be ignored.
+ if (mKeyguardView.getCurrentSecurityMode() == SecurityMode.Password) {
+ View messageArea = mKeyguardView.findViewById(R.id.keyguard_message_area);
+ top += messageArea.getTop();
+ }
+ return top;
+ }
+
protected void ensureView() {
// Removal of the view might be deferred to reduce unlock latency,
// in this case we need to force the removal, otherwise we'll
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 19e8295..3d7067d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -114,6 +114,11 @@
private boolean mTracking;
/**
+ * Distance in pixels between the top of the screen and the first view of the bouncer.
+ */
+ private int mBouncerTop;
+
+ /**
* Refreshes the dimension values.
*/
public void loadDimens(Resources res) {
@@ -129,7 +134,7 @@
public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
float expandedHeight, float maxPanelHeight, int parentHeight, int keyguardStatusHeight,
- float dark, boolean secure, boolean tracking) {
+ float dark, boolean secure, boolean tracking, int bouncerTop) {
mMinTopMargin = minTopMargin + mContainerTopPadding;
mMaxShadeBottom = maxShadeBottom;
mNotificationStackHeight = notificationStackHeight;
@@ -140,6 +145,7 @@
mDarkAmount = dark;
mCurrentlySecure = secure;
mTracking = tracking;
+ mBouncerTop = bouncerTop;
}
public void run(Result result) {
@@ -189,8 +195,10 @@
private int getClockY() {
// Dark: Align the bottom edge of the clock at about half of the screen:
final float clockYDark = getMaxClockY() + burnInPreventionOffsetY();
- float clockYRegular = getExpandedClockPosition();
- float clockYTarget = mCurrentlySecure ? mMinTopMargin : -mKeyguardStatusHeight;
+ final float clockYRegular = getExpandedClockPosition();
+ final boolean hasEnoughSpace = mMinTopMargin + mKeyguardStatusHeight < mBouncerTop;
+ float clockYTarget = mCurrentlySecure && hasEnoughSpace ?
+ mMinTopMargin : -mKeyguardStatusHeight;
// Move clock up while collapsing the shade
float shadeExpansion = mExpandedHeight / mMaxPanelHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 9063dea..b6a11f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -150,7 +150,7 @@
// showAmbient == show in shade but not shelf
if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(
- entry.key)) {
+ entry.notification)) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index cccda90..27ca0d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -160,6 +160,7 @@
protected int mQsMinExpansionHeight;
protected int mQsMaxExpansionHeight;
private int mQsPeekHeight;
+ private int mBouncerTop;
private boolean mStackScrollerOverscrolling;
private boolean mQsExpansionFromOverscroll;
private float mLastOverscroll;
@@ -476,7 +477,8 @@
mKeyguardStatusView.getHeight(),
mDarkAmount,
mStatusBar.isKeyguardCurrentlySecure(),
- mTracking);
+ mTracking,
+ mBouncerTop);
mClockPositionAlgorithm.run(mClockPositionResult);
if (animate || mClockAnimator != null) {
startClockAnimation(mClockPositionResult.clockX, mClockPositionResult.clockY);
@@ -550,6 +552,11 @@
return count;
}
+ public void setBouncerTop(int bouncerTop) {
+ mBouncerTop = bouncerTop;
+ positionClockAndNotifications();
+ }
+
private void startClockAnimation(int x, int y) {
if (mClockAnimationTargetX == x && mClockAnimationTargetY == y) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index c326fee..33c3ee9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -363,16 +363,16 @@
zenDescription = mContext.getString(R.string.interruption_level_priority);
}
- if (DndTile.isVisible(mContext) && !DndTile.isCombinedIcon(mContext)
- && audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
- volumeVisible = true;
- volumeIconId = R.drawable.stat_sys_ringer_silent;
- volumeDescription = mContext.getString(R.string.accessibility_ringer_silent);
- } else if (zen != Global.ZEN_MODE_NO_INTERRUPTIONS && zen != Global.ZEN_MODE_ALARMS &&
+ if (zen != Global.ZEN_MODE_NO_INTERRUPTIONS && zen != Global.ZEN_MODE_ALARMS &&
audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
volumeVisible = true;
volumeIconId = R.drawable.stat_sys_ringer_vibrate;
volumeDescription = mContext.getString(R.string.accessibility_ringer_vibrate);
+ } else if (zen != Global.ZEN_MODE_NO_INTERRUPTIONS && zen != Global.ZEN_MODE_ALARMS &&
+ audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
+ volumeVisible = true;
+ volumeIconId = R.drawable.stat_sys_ringer_silent;
+ volumeDescription = mContext.getString(R.string.accessibility_ringer_silent);
}
if (zenVisible) {
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 c03ecb3..beeba83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -562,7 +562,7 @@
};
private KeyguardUserSwitcher mKeyguardUserSwitcher;
- private UserSwitcherController mUserSwitcherController;
+ protected UserSwitcherController mUserSwitcherController;
private NetworkController mNetworkController;
private KeyguardMonitorImpl mKeyguardMonitor
= (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
@@ -588,7 +588,7 @@
}
};
private boolean mNoAnimationOnNextBarModeChange;
- private FalsingManager mFalsingManager;
+ protected FalsingManager mFalsingManager;
private final KeyguardUpdateMonitorCallback mUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@@ -3448,6 +3448,13 @@
return updateIsKeyguard();
}
+ /**
+ * @return True if StatusBar state is FULLSCREEN_USER_SWITCHER.
+ */
+ public boolean isFullScreenUserSwitcherState() {
+ return mState == StatusBarState.FULLSCREEN_USER_SWITCHER;
+ }
+
private boolean updateIsKeyguard() {
boolean wakeAndUnlocking = mFingerprintUnlockController.getMode()
== FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 5975608..6b6ea10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -135,26 +135,34 @@
mFingerprintUnlockController = fingerprintUnlockController;
mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry);
+ mContainer.addOnLayoutChangeListener(this::onContainerLayout);
mNotificationPanelView = notificationPanelView;
notificationPanelView.setExpansionListener(this::onPanelExpansionChanged);
}
+ private void onContainerLayout(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ mNotificationPanelView.setBouncerTop(mBouncer.getTop());
+ }
+
private void onPanelExpansionChanged(float expansion, boolean tracking) {
// We don't want to translate the bounce when:
// • Keyguard is occluded, because we're in a FLAG_SHOW_WHEN_LOCKED activity and need to
// conserve the original animation.
// • The user quickly taps on the display and we show "swipe up to unlock."
// • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY
+ // • Full-screen user switcher is displayed.
final boolean noLongerTracking = mLastTracking != tracking && !tracking;
if (mOccluded || mNotificationPanelView.isUnlockHintRunning()
- || mBouncer.willDismissWithAction()) {
+ || mBouncer.willDismissWithAction()
+ || mStatusBar.isFullScreenUserSwitcherState()) {
mBouncer.setExpansion(0);
} else if (mShowing && mStatusBar.isKeyguardCurrentlySecure() && !mDozing) {
mBouncer.setExpansion(expansion);
if (expansion == 1) {
mBouncer.onFullyHidden();
} else if (!mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
- mBouncer.show(false /* resetSecuritySelection */, false /* notifyFalsing */);
+ mBouncer.show(false /* resetSecuritySelection */, false /* animated */);
} else if (noLongerTracking) {
// Notify that falsing manager should stop its session when user stops touching,
// even before the animation ends, to guarantee that we're not recording sensitive
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 7ffca173..d527465 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -323,10 +323,10 @@
if (notif != null) {
mNotificationManager.notifyAsUser(vol.getId(), SystemMessage.NOTE_STORAGE_PUBLIC,
- notif, UserHandle.ALL);
+ notif, UserHandle.of(vol.getMountUserId()));
} else {
mNotificationManager.cancelAsUser(vol.getId(), SystemMessage.NOTE_STORAGE_PUBLIC,
- UserHandle.ALL);
+ UserHandle.of(vol.getMountUserId()));
}
}
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 107ce1e..a6b09ce 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -75,7 +75,7 @@
android.test.runner \
telephony-common \
android.test.base \
-
+ android.car
LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui:com.android.keyguard
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
index 2000bff..5e27fde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
@@ -35,6 +35,7 @@
import android.app.NotificationChannel;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.media.session.MediaSession;
import android.os.Bundle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -61,6 +62,7 @@
private static final int UID_NORMAL = 123;
private static final int UID_ALLOW_DURING_SETUP = 456;
private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
+ private static final String TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY = "exempt";
private final StatusBarNotification mMockStatusBarNotification =
mock(StatusBarNotification.class);
@@ -226,12 +228,21 @@
public void testSuppressSystemAlertNotification() {
when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+ StatusBarNotification sbn = mRow.getEntry().notification;
+ Bundle bundle = new Bundle();
+ bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
+ sbn.getNotification().extras = bundle;
assertTrue(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
}
@Test
public void testDoNotSuppressSystemAlertNotification() {
+ StatusBarNotification sbn = mRow.getEntry().notification;
+ Bundle bundle = new Bundle();
+ bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
+ sbn.getNotification().extras = bundle;
+
when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
@@ -249,7 +260,24 @@
}
@Test
+ public void testDoNotSuppressMalformedSystemAlertNotification() {
+ when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+
+ // missing extra
+ assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
+
+ StatusBarNotification sbn = mRow.getEntry().notification;
+ Bundle bundle = new Bundle();
+ bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {});
+ sbn.getNotification().extras = bundle;
+
+ // extra missing values
+ assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
+ }
+
+ @Test
public void testShouldFilterHiddenNotifications() {
+ initStatusBarNotification(false);
// setup
when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
@@ -264,6 +292,33 @@
assertFalse(mNotificationData.shouldFilterOut(mMockStatusBarNotification));
}
+ @Test
+ public void testIsExemptFromDndVisualSuppression_foreground() {
+ initStatusBarNotification(false);
+ when(mMockStatusBarNotification.getKey()).thenReturn(
+ TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
+ Notification n = mMockStatusBarNotification.getNotification();
+ n.flags = Notification.FLAG_FOREGROUND_SERVICE;
+
+ assertTrue(mNotificationData.isExemptFromDndVisualSuppression(mMockStatusBarNotification));
+ assertFalse(mNotificationData.shouldSuppressAmbient(mMockStatusBarNotification));
+ }
+
+ @Test
+ public void testIsExemptFromDndVisualSuppression_media() {
+ initStatusBarNotification(false);
+ when(mMockStatusBarNotification.getKey()).thenReturn(
+ TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
+ Notification n = mMockStatusBarNotification.getNotification();
+ Notification.Builder nb = Notification.Builder.recoverBuilder(mContext, n);
+ nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+ n = nb.build();
+ when(mMockStatusBarNotification.getNotification()).thenReturn(n);
+
+ assertTrue(mNotificationData.isExemptFromDndVisualSuppression(mMockStatusBarNotification));
+ assertFalse(mNotificationData.shouldSuppressAmbient(mMockStatusBarNotification));
+ }
+
private void initStatusBarNotification(boolean allowDuringSetup) {
Bundle bundle = new Bundle();
bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
@@ -293,6 +348,13 @@
outRanking.getImportance(), outRanking.getImportanceExplanation(),
outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
+ } else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) {
+ outRanking.populate(key, outRanking.getRank(),
+ outRanking.matchesInterruptionFilter(),
+ outRanking.getVisibilityOverride(), 255,
+ outRanking.getImportance(), outRanking.getImportanceExplanation(),
+ outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
+ outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
} else {
outRanking.populate(key, outRanking.getRank(),
outRanking.matchesInterruptionFilter(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 7dda8b2..c2cb5b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -218,6 +218,18 @@
}
@Test
+ public void testBindNotification_DefaultChannelUsesChannelNameIfMoreChannelsExist()
+ throws Exception {
+ // Package has one channel by default.
+ when(mMockINotificationManager.getNumNotificationChannelsForPackage(
+ eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, null);
+ final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
+ assertEquals(VISIBLE, textView.getVisibility());
+ }
+
+ @Test
public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null,
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 4a66bb7..b31a2dc 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
@@ -41,6 +41,7 @@
import android.app.StatusBarManager;
import android.app.trust.TrustManager;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.os.Binder;
@@ -71,6 +72,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.ActivatableNotificationView;
@@ -91,6 +93,7 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
@@ -98,6 +101,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import org.junit.Before;
@@ -134,6 +138,7 @@
@Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private NotificationListener mNotificationListener;
@Mock private KeyguardViewMediator mKeyguardViewMediator;
+ @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
private TestableStatusBar mStatusBar;
private FakeMetricsLogger mMetricsLogger;
@@ -146,9 +151,7 @@
MockitoAnnotations.initMocks(this);
mDependency.injectMockDependency(AssistManager.class);
mDependency.injectMockDependency(DeviceProvisionedController.class);
- mDependency.injectMockDependency(NotificationGroupManager.class);
mDependency.injectMockDependency(NotificationGutsManager.class);
- mDependency.injectMockDependency(NotificationRemoteInputManager.class);
mDependency.injectMockDependency(NotificationMediaManager.class);
mDependency.injectMockDependency(ForegroundServiceController.class);
mDependency.injectTestDependency(NotificationViewHierarchyManager.class,
@@ -202,7 +205,12 @@
mPowerManager, mNotificationPanelView, mBarService, mNotificationListener,
mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager,
mEntryManager, mScrimController, mFingerprintUnlockController,
- mock(ActivityLaunchAnimator.class), mKeyguardViewMediator);
+ mock(ActivityLaunchAnimator.class), mKeyguardViewMediator,
+ mock(NotificationRemoteInputManager.class), mock(NotificationGroupManager.class),
+ mock(FalsingManager.class), mock(StatusBarWindowManager.class),
+ mock(NotificationIconAreaController.class), mock(DozeScrimController.class),
+ mock(NotificationShelf.class), mLockscreenUserManager,
+ mock(CommandQueue.class));
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mContext.getComponents();
mEntryManager.setUpForTest(mStatusBar, mStackScroller, mStatusBar, mHeadsUpManager,
@@ -529,11 +537,7 @@
@Test
@RunWithLooper(setAsMainLooper = true)
public void testUpdateKeyguardState_DoesNotCrash() {
- mStatusBar.mStatusBarWindow = mock(StatusBarWindowView.class);
mStatusBar.mState = StatusBarState.KEYGUARD;
- mStatusBar.mDozeScrimController = mock(DozeScrimController.class);
- mStatusBar.mNotificationIconAreaController = mock(NotificationIconAreaController.class);
- mStatusBar.mLockscreenUserManager = mock(NotificationLockscreenUserManager.class);
when(mStatusBar.mLockscreenUserManager.getCurrentProfiles()).thenReturn(
new SparseArray<>());
mStatusBar.updateKeyguardState(false, false);
@@ -541,9 +545,6 @@
@Test
public void testFingerprintNotification_UpdatesScrims() {
- mStatusBar.mStatusBarWindowManager = mock(StatusBarWindowManager.class);
- mStatusBar.mDozeScrimController = mock(DozeScrimController.class);
- mStatusBar.mNotificationIconAreaController = mock(NotificationIconAreaController.class);
mStatusBar.notifyFpAuthModeChanged();
verify(mScrimController).transitionTo(any(), any());
}
@@ -629,6 +630,32 @@
verify(mStackScroller).changeViewPosition(any(FooterView.class), eq(-1 /* end */));
}
+ @Test
+ public void testSetState_changesIsFullScreenUserSwitcherState() {
+ mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ assertFalse(mStatusBar.isFullScreenUserSwitcherState());
+
+ mStatusBar.setBarStateForTest(StatusBarState.FULLSCREEN_USER_SWITCHER);
+ assertTrue(mStatusBar.isFullScreenUserSwitcherState());
+ }
+
+ @Test
+ public void testShowKeyguardImplementation_setsState() {
+ when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
+
+ mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+
+ // By default, showKeyguardImpl sets state to KEYGUARD.
+ mStatusBar.showKeyguardImpl();
+ assertTrue(mStatusBar.mState == StatusBarState.KEYGUARD);
+
+ // If useFullscreenUserSwitcher is true, state is set to FULLSCREEN_USER_SWITCHER.
+ mStatusBar.mUserSwitcherController = mock(UserSwitcherController.class);
+ when(mStatusBar.mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true);
+ mStatusBar.showKeyguardImpl();
+ assertTrue(mStatusBar.mState == StatusBarState.FULLSCREEN_USER_SWITCHER);
+ }
+
static class TestableStatusBar extends StatusBar {
public TestableStatusBar(StatusBarKeyguardViewManager man,
UnlockMethodCache unlock, KeyguardIndicationController key,
@@ -640,7 +667,16 @@
NotificationViewHierarchyManager viewHierarchyManager,
TestableNotificationEntryManager entryManager, ScrimController scrimController,
FingerprintUnlockController fingerprintUnlockController,
- ActivityLaunchAnimator launchAnimator, KeyguardViewMediator keyguardViewMediator) {
+ ActivityLaunchAnimator launchAnimator, KeyguardViewMediator keyguardViewMediator,
+ NotificationRemoteInputManager notificationRemoteInputManager,
+ NotificationGroupManager notificationGroupManager,
+ FalsingManager falsingManager,
+ StatusBarWindowManager statusBarWindowManager,
+ NotificationIconAreaController notificationIconAreaController,
+ DozeScrimController dozeScrimController,
+ NotificationShelf notificationShelf,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ CommandQueue commandQueue) {
mStatusBarKeyguardViewManager = man;
mUnlockMethodCache = unlock;
mKeyguardIndicationController = key;
@@ -660,6 +696,15 @@
mActivityLaunchAnimator = launchAnimator;
mKeyguardViewMediator = keyguardViewMediator;
mClearAllEnabled = true;
+ mRemoteInputManager = notificationRemoteInputManager;
+ mGroupManager = notificationGroupManager;
+ mFalsingManager = falsingManager;
+ mStatusBarWindowManager = statusBarWindowManager;
+ mNotificationIconAreaController = notificationIconAreaController;
+ mDozeScrimController = dozeScrimController;
+ mNotificationShelf = notificationShelf;
+ mLockscreenUserManager = notificationLockscreenUserManager;
+ mCommandQueue = commandQueue;
}
private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8941b49..de112f9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -853,11 +853,7 @@
if (mSecurityPolicy.findA11yWindowInfoById(windowId) == null) {
return null;
}
- IBinder token = mGlobalWindowTokens.get(windowId);
- if (token != null) {
- return token;
- }
- return getCurrentUserStateLocked().mWindowTokens.get(windowId);
+ return findWindowTokenLocked(windowId);
}
}
@@ -2527,6 +2523,14 @@
getInteractionBridge().clearAccessibilityFocusNotLocked(windowId);
}
+ private IBinder findWindowTokenLocked(int windowId) {
+ IBinder token = mGlobalWindowTokens.get(windowId);
+ if (token != null) {
+ return token;
+ }
+ return getCurrentUserStateLocked().mWindowTokens.get(windowId);
+ }
+
private int findWindowIdLocked(IBinder token) {
final int globalIndex = mGlobalWindowTokens.indexOfValue(token);
if (globalIndex >= 0) {
@@ -2986,7 +2990,7 @@
// the accessibility layer reports which are windows
// that a sighted user can touch.
default: {
- return isRetrievalAllowingWindow(event.getWindowId());
+ return isRetrievalAllowingWindowLocked(event.getWindowId());
}
}
}
@@ -3438,7 +3442,8 @@
public boolean canGetAccessibilityNodeInfoLocked(
AbstractAccessibilityServiceConnection service, int windowId) {
- return canRetrieveWindowContentLocked(service) && isRetrievalAllowingWindow(windowId);
+ return canRetrieveWindowContentLocked(service)
+ && isRetrievalAllowingWindowLocked(windowId);
}
public boolean canRetrieveWindowsLocked(AbstractAccessibilityServiceConnection service) {
@@ -3523,17 +3528,40 @@
|| userId == UserHandle.USER_CURRENT_OR_SELF);
}
- private boolean isRetrievalAllowingWindow(int windowId) {
+ private boolean isRetrievalAllowingWindowLocked(int windowId) {
// The system gets to interact with any window it wants.
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
return true;
}
+ if (Binder.getCallingUid() == Process.SHELL_UID) {
+ if (!isShellAllowedToRetrieveWindowLocked(windowId)) {
+ return false;
+ }
+ }
if (windowId == mActiveWindowId) {
return true;
}
return findA11yWindowInfoById(windowId) != null;
}
+ private boolean isShellAllowedToRetrieveWindowLocked(int windowId) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ IBinder windowToken = findWindowTokenLocked(windowId);
+ if (windowToken == null) {
+ return false;
+ }
+ int userId = mWindowManagerService.getWindowOwnerUserId(windowToken);
+ if (userId == UserHandle.USER_NULL) {
+ return false;
+ }
+ return !mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.of(userId));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
public AccessibilityWindowInfo findA11yWindowInfoById(int windowId) {
return mA11yWindowInfoById.get(windowId);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index d4ecd8b..3253f2e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -19,7 +19,6 @@
import static android.Manifest.permission.MANAGE_AUTO_FILL;
import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
-import static com.android.server.autofill.Helper.bundleToString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sFullScreenMode;
import static com.android.server.autofill.Helper.sPartitionMaxCount;
@@ -193,8 +192,7 @@
if (disabledBefore == disabledNow) {
// Nothing changed, do nothing.
if (sDebug) {
- Slog.d(TAG, "Autofill restriction did not change for user " + userId + ": "
- + bundleToString(newRestrictions));
+ Slog.d(TAG, "Autofill restriction did not change for user " + userId);
return;
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index a202aaf..78526f5 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -79,28 +79,6 @@
throw new UnsupportedOperationException("contains static members only");
}
- static void append(StringBuilder builder, Bundle bundle) {
- if (bundle == null || !sVerbose) {
- builder.append("null");
- return;
- }
- final Set<String> keySet = bundle.keySet();
- builder.append("[Bundle with ").append(keySet.size()).append(" extras:");
- for (String key : keySet) {
- final Object value = bundle.get(key);
- builder.append(' ').append(key).append('=');
- builder.append((value instanceof Object[])
- ? Arrays.toString((Objects[]) value) : value);
- }
- builder.append(']');
- }
-
- static String bundleToString(Bundle bundle) {
- final StringBuilder builder = new StringBuilder();
- append(builder, bundle);
- return builder.toString();
- }
-
@Nullable
static AutofillId[] toArray(@Nullable ArraySet<AutofillId> set) {
if (set == null) return null;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 706fb1a..706a6ab 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2502,8 +2502,10 @@
}
pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback);
- pw.print(prefix); pw.print("mClientState: "); pw.println(
- Helper.bundleToString(mClientState));
+ if (mClientState != null) {
+ pw.print(prefix); pw.println("mClientState: "); pw.print(mClientState.getSize()); pw
+ .println(" items");
+ }
pw.print(prefix); pw.print("mCompatMode: "); pw.println(mCompatMode);
pw.print(prefix); pw.print("mUrlBar: ");
if (mUrlBar == null) {
diff --git a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
index 4de2c9b..49fa1cc 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
@@ -21,6 +21,7 @@
import android.provider.Settings;
import android.util.KeyValueListParser;
import android.util.KeyValueSettingObserver;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -31,6 +32,8 @@
* are represented as a comma-delimited key value list.
*/
public class BackupAgentTimeoutParameters extends KeyValueSettingObserver {
+ private static final String TAG = "BackupAgentTimeout";
+
@VisibleForTesting
public static final String SETTING = Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS;
@@ -120,30 +123,50 @@
public long getKvBackupAgentTimeoutMillis() {
synchronized (mLock) {
+ if (BackupManagerService.DEBUG_SCHEDULING) {
+ Slog.v(TAG, "getKvBackupAgentTimeoutMillis(): " + mKvBackupAgentTimeoutMillis);
+ }
return mKvBackupAgentTimeoutMillis;
}
}
public long getFullBackupAgentTimeoutMillis() {
synchronized (mLock) {
+ if (BackupManagerService.DEBUG_SCHEDULING) {
+ Slog.v(TAG, "getFullBackupAgentTimeoutMillis(): " + mFullBackupAgentTimeoutMillis);
+ }
return mFullBackupAgentTimeoutMillis;
}
}
public long getSharedBackupAgentTimeoutMillis() {
synchronized (mLock) {
+ if (BackupManagerService.DEBUG_SCHEDULING) {
+ Slog.v(
+ TAG,
+ "getSharedBackupAgentTimeoutMillis(): " + mSharedBackupAgentTimeoutMillis);
+ }
return mSharedBackupAgentTimeoutMillis;
}
}
public long getRestoreAgentTimeoutMillis() {
synchronized (mLock) {
+ if (BackupManagerService.DEBUG_SCHEDULING) {
+ Slog.v(TAG, "getRestoreAgentTimeoutMillis(): " + mRestoreAgentTimeoutMillis);
+ }
return mRestoreAgentTimeoutMillis;
}
}
public long getRestoreAgentFinishedTimeoutMillis() {
synchronized (mLock) {
+ if (BackupManagerService.DEBUG_SCHEDULING) {
+ Slog.v(
+ TAG,
+ "getRestoreAgentFinishedTimeoutMillis(): "
+ + mRestoreAgentFinishedTimeoutMillis);
+ }
return mRestoreAgentFinishedTimeoutMillis;
}
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d6f6c6c..bd51af2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -215,13 +215,6 @@
// Timeout interval for deciding that a bind or clear-data has taken too long
private static final long TIMEOUT_INTERVAL = 10 * 1000;
- // Timeout intervals for agent backup & restore operations
- public static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
- public static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
- public static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
- public static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
- public static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000;
-
// User confirmation timeout for a full backup/restore operation. It's this long in
// order to give them time to enter the backup password.
private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
@@ -232,6 +225,7 @@
private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
private BackupManagerConstants mConstants;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private Context mContext;
private PackageManager mPackageManager;
private IPackageManager mPackageManagerBinder;
@@ -315,6 +309,10 @@
return mConstants;
}
+ public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
+ return mAgentTimeoutParameters;
+ }
+
public Context getContext() {
return mContext;
}
@@ -799,6 +797,10 @@
mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
+ mAgentTimeoutParameters = new
+ BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
+ mAgentTimeoutParameters.start();
+
// spin up the backup/restore handler thread
mBackupHandler = new BackupHandler(this, backupThread.getLooper());
@@ -3407,7 +3409,7 @@
}
mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
- TIMEOUT_RESTORE_INTERVAL);
+ mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
}
return mActiveRestoreSession;
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
index 7b021c6..aabe7f6 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
@@ -191,4 +191,7 @@
void dump(FileDescriptor fd, PrintWriter pw, String[] args);
IBackupManager getBackupManagerBinder();
+
+ // Gets access to the backup/restore agent timeout parameters.
+ BackupAgentTimeoutParameters getAgentTimeoutParameters();
}
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 4755877..7f0030a 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -4,8 +4,8 @@
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
+
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
@@ -19,6 +19,7 @@
import android.os.SELinux;
import android.util.Slog;
+import com.android.internal.util.Preconditions;
import com.android.server.backup.utils.FullBackupUtils;
import libcore.io.IoUtils;
@@ -59,6 +60,7 @@
private ParcelFileDescriptor mSavedState;
private ParcelFileDescriptor mBackupData;
private ParcelFileDescriptor mNewState;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo,
BackupManagerServiceInterface backupManagerService, PackageManager packageManager,
@@ -81,6 +83,9 @@
pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME);
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
}
public void backupOnePackage() throws IOException {
@@ -148,8 +153,9 @@
// Return true on backup success, false otherwise
private boolean invokeAgentForAdbBackup(String packageName, IBackupAgent agent) {
int token = mBackupManagerService.generateRandomIntegerToken();
+ long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
try {
- mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null,
+ mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
OP_TYPE_BACKUP_WAIT);
// Start backup and wait for BackupManagerService to get callback for success or timeout
@@ -231,14 +237,14 @@
}
private void writeBackupData() throws IOException {
-
int token = mBackupManagerService.generateRandomIntegerToken();
+ long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
ParcelFileDescriptor[] pipes = null;
try {
pipes = ParcelFileDescriptor.createPipe();
- mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null,
+ mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
OP_TYPE_BACKUP_WAIT);
// We will have to create a runnable that will read the manifest and backup data we
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 0582aba..5694659 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -25,9 +25,6 @@
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
-import static com.android.server.backup.BackupManagerService
- .TIMEOUT_SHARED_BACKUP_INTERVAL;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
@@ -44,9 +41,11 @@
import android.util.Slog;
import android.util.StringBuilderPrinter;
+import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
-import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.utils.FullBackupUtils;
import java.io.BufferedOutputStream;
@@ -75,6 +74,7 @@
private final long mQuota;
private final int mOpToken;
private final int mTransportFlags;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
class FullBackupRunner implements Runnable {
@@ -137,8 +137,8 @@
final boolean isSharedStorage =
mPackage.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
final long timeout = isSharedStorage ?
- TIMEOUT_SHARED_BACKUP_INTERVAL :
- TIMEOUT_FULL_BACKUP_INTERVAL;
+ mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() :
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
if (DEBUG) {
Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
@@ -180,6 +180,9 @@
mQuota = quota;
mOpToken = opToken;
mTransportFlags = transportFlags;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
}
public int preflightCheck() throws RemoteException {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index 40b6967..bc7d9fc 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -19,7 +19,6 @@
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
import android.app.backup.IBackupManager;
import android.content.ComponentName;
@@ -33,6 +32,8 @@
import android.util.Slog;
import com.android.internal.backup.IObbBackupService;
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.utils.FullBackupUtils;
@@ -46,10 +47,14 @@
private BackupManagerService backupManagerService;
volatile IObbBackupService mService;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public FullBackupObbConnection(BackupManagerService backupManagerService) {
this.backupManagerService = backupManagerService;
mService = null;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
}
public void establish() {
@@ -75,8 +80,10 @@
try {
pipes = ParcelFileDescriptor.createPipe();
int token = backupManagerService.generateRandomIntegerToken();
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
backupManagerService.prepareOperationTimeout(
- token, TIMEOUT_FULL_BACKUP_INTERVAL, null, OP_TYPE_BACKUP_WAIT);
+ token, fullBackupAgentTimeoutMillis, null, OP_TYPE_BACKUP_WAIT);
mService.backupObbs(pkg.packageName, pipes[1], token,
backupManagerService.getBackupManagerBinder());
FullBackupUtils.routeSocketDataToOutput(pipes[0], out);
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 2c2dd85..a40afc3 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -22,7 +22,6 @@
import static com.android.server.backup.BackupManagerService.OP_PENDING;
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
import android.annotation.Nullable;
import android.app.IBackupAgent;
@@ -43,7 +42,9 @@
import android.util.Slog;
import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FullBackupJob;
import com.android.server.backup.BackupManagerService;
@@ -146,6 +147,7 @@
private volatile boolean mIsDoingBackup;
private volatile boolean mCancelAll;
private final int mCurrentOpToken;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public PerformFullTransportBackupTask(BackupManagerService backupManagerService,
TransportClient transportClient,
@@ -167,6 +169,9 @@
mUserInitiated = userInitiated;
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
mBackupRunnerOpToken = backupManagerService.generateRandomIntegerToken();
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
if (backupManagerService.isBackupOperationInProgress()) {
if (DEBUG) {
@@ -698,9 +703,11 @@
@Override
public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
int result;
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
try {
backupManagerService.prepareOperationTimeout(
- mCurrentOpToken, TIMEOUT_FULL_BACKUP_INTERVAL, this, OP_TYPE_BACKUP_WAIT);
+ mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
backupManagerService.addBackupTrace("preflighting");
if (MORE_DEBUG) {
Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
@@ -713,7 +720,7 @@
// timeout had been produced. In case of a real backstop timeout, mResult
// will still contain the value it was constructed with, AGENT_ERROR, which
// intentionaly falls into the "just report failure" code.
- mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+ mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
long totalSize = mResult.get();
// If preflight timed out, mResult will contain error code as int.
@@ -769,8 +776,10 @@
@Override
public long getExpectedSizeOrErrorCode() {
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
try {
- mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+ mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
return mResult.get();
} catch (InterruptedException e) {
return BackupTransport.NO_MORE_DATA;
@@ -863,8 +872,10 @@
// If preflight succeeded, returns positive number - preflight size,
// otherwise return negative error code.
long getPreflightResultBlocking() {
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
try {
- mPreflightLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+ mPreflightLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
if (mIsCancelled) {
return BackupManager.ERROR_BACKUP_CANCELLED;
}
@@ -879,8 +890,10 @@
}
int getBackupResultBlocking() {
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
try {
- mBackupLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+ mBackupLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
if (mIsCancelled) {
return BackupManager.ERROR_BACKUP_CANCELLED;
}
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 136fada..69f08ae 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -19,7 +19,6 @@
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
import android.app.backup.RestoreSet;
import android.content.Intent;
@@ -33,7 +32,9 @@
import android.util.Slog;
import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
@@ -81,10 +82,14 @@
public static final int MSG_OP_COMPLETE = 21;
private final BackupManagerService backupManagerService;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public BackupHandler(BackupManagerService backupManagerService, Looper looper) {
super(looper);
this.backupManagerService = backupManagerService;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
}
public void handleMessage(Message msg) {
@@ -322,7 +327,8 @@
// Done: reset the session timeout clock
removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
- sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
+ sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
+ mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
params.listener.onFinished(callerLogString);
}
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index 11394e66..ac605b1 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -24,7 +24,6 @@
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL;
import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
@@ -55,8 +54,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.KeyValueBackupJob;
@@ -142,6 +143,7 @@
private boolean mFinished;
private final boolean mUserInitiated;
private final boolean mNonIncremental;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private volatile boolean mCancelAll;
@@ -162,6 +164,9 @@
mPendingFullBackups = pendingFullBackups;
mUserInitiated = userInitiated;
mNonIncremental = nonIncremental;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
mStateDir = new File(backupManagerService.getBaseStateDir(), dirName);
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
@@ -711,8 +716,10 @@
// Initiate the target's backup pass
backupManagerService.addBackupTrace("setting timeout");
+ long kvBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
backupManagerService.prepareOperationTimeout(
- mEphemeralOpToken, TIMEOUT_BACKUP_INTERVAL, this, OP_TYPE_BACKUP_WAIT);
+ mEphemeralOpToken, kvBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
backupManagerService.addBackupTrace("calling agent doBackup()");
agent.doBackup(
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index e4f3a9d..a8c7ce6 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -18,10 +18,11 @@
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
import android.util.Slog;
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
@@ -37,18 +38,24 @@
private BackupManagerService backupManagerService;
final CountDownLatch mLatch;
private final int mCurrentOpToken;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public AdbRestoreFinishedLatch(BackupManagerService backupManagerService,
int currentOpToken) {
this.backupManagerService = backupManagerService;
mLatch = new CountDownLatch(1);
mCurrentOpToken = currentOpToken;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
}
void await() {
boolean latched = false;
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
try {
- latched = mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+ latched = mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Slog.w(TAG, "Interrupted!");
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index c1a1c1d..6bc7530 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -23,9 +23,6 @@
import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
-import static com.android.server.backup.BackupManagerService
- .TIMEOUT_SHARED_BACKUP_INTERVAL;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
import android.app.ApplicationThreadConstants;
@@ -40,13 +37,17 @@
import android.content.pm.Signature;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Slog;
+import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FileMetadata;
import com.android.server.backup.KeyValueAdbRestoreEngine;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.fullbackup.FullBackupObbConnection;
import com.android.server.backup.utils.BytesReadListener;
import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
@@ -56,8 +57,11 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
/**
* Full restore engine, used by both adb restore and transport-based full restore.
@@ -121,6 +125,8 @@
final int mEphemeralOpToken;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+
public FullRestoreEngine(BackupManagerService backupManagerService,
BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
@@ -135,6 +141,9 @@
mAllowObbs = allowObbs;
mBuffer = new byte[32 * 1024];
mBytes = 0;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
}
public IBackupAgent getAgent() {
@@ -320,12 +329,17 @@
pkg, 0);
// If we haven't sent any data to this app yet, we probably
- // need to clear it first. Check that.
+ // need to clear it first. Check that.
if (!mClearedPackages.contains(pkg)) {
- // apps with their own backup agents are
- // responsible for coherently managing a full
- // restore.
- if (mTargetApp.backupAgentName == null) {
+ // Apps with their own backup agents are responsible for coherently
+ // managing a full restore.
+ // In some rare cases they can't, especially in case of deferred
+ // restore. In this case check whether this app should be forced to
+ // clear up.
+ // TODO: Fix this properly with manifest parameter.
+ boolean forceClear = shouldForceClearAppDataOnFullRestore(
+ mTargetApp.packageName);
+ if (mTargetApp.backupAgentName == null || forceClear) {
if (DEBUG) {
Slog.d(TAG,
"Clearing app data preparatory to full restore");
@@ -381,8 +395,8 @@
long toCopy = info.size;
final boolean isSharedStorage = pkg.equals(SHARED_BACKUP_AGENT_PACKAGE);
final long timeout = isSharedStorage ?
- TIMEOUT_SHARED_BACKUP_INTERVAL :
- TIMEOUT_RESTORE_INTERVAL;
+ mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() :
+ mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
try {
mBackupManagerService.prepareOperationTimeout(token,
timeout,
@@ -623,6 +637,24 @@
return true;
}
+ /**
+ * Returns whether the package is in the list of the packages for which clear app data should
+ * be called despite the fact that they have backup agent.
+ *
+ * <p>The list is read from {@link Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}.
+ */
+ private boolean shouldForceClearAppDataOnFullRestore(String packageName) {
+ String packageListString = Settings.Secure.getString(
+ mBackupManagerService.getContext().getContentResolver(),
+ Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
+ if (TextUtils.isEmpty(packageListString)) {
+ return false;
+ }
+
+ List<String> packages = Arrays.asList(packageListString.split(";"));
+ return packages.contains(packageName);
+ }
+
void sendOnRestorePackage(String name) {
if (mObserver != null) {
try {
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index dacde0b..77163d3 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -16,8 +16,6 @@
package com.android.server.backup.restore;
-import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
-import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
@@ -28,8 +26,8 @@
import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
import android.app.ApplicationThreadConstants;
@@ -48,7 +46,9 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.FileMetadata;
import com.android.server.backup.KeyValueAdbRestoreEngine;
@@ -101,6 +101,7 @@
private byte[] mWidgetData = null;
private long mBytes;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
// Runner that can be placed on a separate thread to do in-process invocation
// of the "restore finished" API asynchronously. Used by adb restore.
@@ -155,6 +156,9 @@
mAgentPackage = null;
mTargetApp = null;
mObbConnection = new FullBackupObbConnection(backupManagerService);
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
// Which packages we've already wiped data on. We prepopulate this
// with a whitelist of packages known to be unclearable.
@@ -643,9 +647,11 @@
if (okay) {
boolean agentSuccess = true;
long toCopy = info.size;
+ long restoreAgentTimeoutMillis =
+ mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
try {
mBackupManagerService.prepareOperationTimeout(
- token, TIMEOUT_RESTORE_INTERVAL, null, OP_TYPE_RESTORE_WAIT);
+ token, restoreAgentTimeoutMillis, null, OP_TYPE_RESTORE_WAIT);
if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
if (DEBUG) {
@@ -820,10 +826,12 @@
// In the adb restore case, we do restore-finished here
if (doRestoreFinished) {
final int token = mBackupManagerService.generateRandomIntegerToken();
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
mBackupManagerService, token);
mBackupManagerService.prepareOperationTimeout(
- token, TIMEOUT_FULL_BACKUP_INTERVAL, latch, OP_TYPE_RESTORE_WAIT);
+ token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT);
if (mTargetApp.processName.equals("system")) {
if (MORE_DEBUG) {
Slog.d(TAG, "system agent - restoreFinished on thread");
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 4b467e5..12d72d8 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -23,9 +23,6 @@
import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService
- .TIMEOUT_RESTORE_FINISHED_INTERVAL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
@@ -56,9 +53,11 @@
import android.util.Slog;
import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.BackupUtils;
import com.android.server.backup.PackageManagerBackupAgent;
@@ -160,6 +159,7 @@
ParcelFileDescriptor mNewState;
private final int mEphemeralOpToken;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
// This task can assume that the wakelock is properly held for it and doesn't have to worry
// about releasing it.
@@ -190,6 +190,9 @@
mFinished = false;
mDidLaunch = false;
mListener = listener;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
if (targetPackage != null) {
// Single package restore
@@ -760,8 +763,9 @@
// Kick off the restore, checking for hung agents. The timeout or
// the operationComplete() callback will schedule the next step,
// so we do not do that here.
+ long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
backupManagerService.prepareOperationTimeout(
- mEphemeralOpToken, TIMEOUT_RESTORE_INTERVAL, this, OP_TYPE_RESTORE_WAIT);
+ mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT);
mAgent.doRestore(mBackupData, appVersionCode, mNewState,
mEphemeralOpToken, backupManagerService.getBackupManagerBinder());
} catch (Exception e) {
@@ -813,9 +817,11 @@
Slog.d(TAG, "restoreFinished packageName=" + mCurrentPackage.packageName);
}
try {
+ long restoreAgentFinishedTimeoutMillis =
+ mAgentTimeoutParameters.getRestoreAgentFinishedTimeoutMillis();
backupManagerService
.prepareOperationTimeout(mEphemeralOpToken,
- TIMEOUT_RESTORE_FINISHED_INTERVAL, this,
+ restoreAgentFinishedTimeoutMillis, this,
OP_TYPE_RESTORE_WAIT);
mAgent.doRestoreFinished(mEphemeralOpToken,
backupManagerService.getBackupManagerBinder());
@@ -1109,9 +1115,10 @@
} else {
// We were invoked via an active restore session, not by the Package
// Manager, so start up the session timeout again.
+ long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
backupManagerService.getBackupHandler().sendEmptyMessageDelayed(
MSG_RESTORE_SESSION_TIMEOUT,
- TIMEOUT_RESTORE_INTERVAL);
+ restoreAgentTimeoutMillis);
}
// Kick off any work that may be needed regarding app widget restores
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e3584e6..08e5d44 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2873,13 +2873,15 @@
}
/**
- * Encapsulates the globla setting "hidden_api_blacklist_exemptions", including tracking the
+ * Encapsulates the global setting "hidden_api_blacklist_exemptions", including tracking the
* latest value via a content observer.
*/
static class HiddenApiBlacklist extends ContentObserver {
private final Context mContext;
private boolean mBlacklistDisabled;
+ private String mExemptionsStr;
+ private List<String> mExemptions = Collections.emptyList();
public HiddenApiBlacklist(Handler handler, Context context) {
super(handler);
@@ -2895,8 +2897,22 @@
}
private void update() {
- mBlacklistDisabled = "*".equals(Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS));
+ String exemptions = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS);
+ if (!TextUtils.equals(exemptions, mExemptionsStr)) {
+ mExemptionsStr = exemptions;
+ if ("*".equals(exemptions)) {
+ mBlacklistDisabled = true;
+ mExemptions = Collections.emptyList();
+ } else {
+ mBlacklistDisabled = false;
+ mExemptions = TextUtils.isEmpty(exemptions)
+ ? Collections.emptyList()
+ : Arrays.asList(exemptions.split(","));
+ }
+ zygoteProcess.setApiBlacklistExemptions(mExemptions);
+ }
+
}
boolean isDisabled() {
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index 106b37f..bef650b 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -285,6 +285,16 @@
return false;
}
+ /**
+ * @return the root task of the lock task.
+ */
+ TaskRecord getRootTask() {
+ if (mLockTaskModeTasks.isEmpty()) {
+ return null;
+ }
+ return mLockTaskModeTasks.get(0);
+ }
+
private boolean isLockTaskModeViolationInternal(TaskRecord task, boolean isNewClearTask) {
// TODO: Double check what's going on here. If the task is already in lock task mode, it's
// likely whitelisted, so will return false below.
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index fcf00ce..1d305fb 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -1155,6 +1155,11 @@
}
}
+ // If we're in lock task mode, ignore the root task
+ if (task == mService.mLockTaskController.getRootTask()) {
+ return false;
+ }
+
return true;
}
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index 73a7c3eb..9df321c 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -42,18 +42,13 @@
class RecentsAnimation implements RecentsAnimationCallbacks {
private static final String TAG = RecentsAnimation.class.getSimpleName();
- private static final int RECENTS_ANIMATION_TIMEOUT = 10 * 1000;
-
private final ActivityManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
private final ActivityStartController mActivityStartController;
private final WindowManagerService mWindowManager;
private final UserController mUserController;
- private final Handler mHandler;
private final int mCallingPid;
- private final Runnable mCancelAnimationRunnable;
-
// The stack to restore the home stack behind when the animation is finished
private ActivityStack mRestoreHomeBehindStack;
@@ -63,16 +58,9 @@
mService = am;
mStackSupervisor = stackSupervisor;
mActivityStartController = activityStartController;
- mHandler = new Handler(mStackSupervisor.mLooper);
mWindowManager = wm;
mUserController = userController;
mCallingPid = callingPid;
-
- mCancelAnimationRunnable = () -> {
- // The caller has not finished the animation in a predefined amount of time, so
- // force-cancel the animation
- mWindowManager.cancelRecentsAnimation();
- };
}
void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
@@ -133,10 +121,6 @@
// duration of the gesture that is driven by the recents component
homeActivity.mLaunchTaskBehind = true;
- // Post a timeout for the animation. This needs to happen before initializing the
- // recents animation on the WM side since we may decide to cancel the animation there
- mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
-
// Fetch all the surface controls and pass them to the client to get the animation
// started
mWindowManager.cancelRecentsAnimation();
@@ -157,7 +141,6 @@
@Override
public void onAnimationFinished(boolean moveHomeToTop) {
- mHandler.removeCallbacks(mCancelAnimationRunnable);
synchronized (mService) {
if (mWindowManager.getRecentsAnimationController() == null) return;
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
index 32b1d1a..7a6d964 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
@@ -17,6 +17,7 @@
package com.android.server.broadcastradio.hal1;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.ProgramList;
@@ -87,8 +88,9 @@
mTuner.close();
}
- void startProgramListUpdates(@NonNull ProgramList.Filter filter) {
- mProgramListFilter.set(Objects.requireNonNull(filter));
+ void startProgramListUpdates(@Nullable ProgramList.Filter filter) {
+ if (filter == null) filter = new ProgramList.Filter();
+ mProgramListFilter.set(filter);
sendProgramListUpdate();
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index 6919282..9730c9a 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -232,6 +232,7 @@
* HAL implementation instance. */
1, // numTuners
1, // numAudioSources
+ false, // isInitializationRequired
false, // isCaptureSupported
amfmConfigToBands(amfmConfig),
@@ -374,7 +375,9 @@
);
}
- static @NonNull ProgramFilter programFilterToHal(@NonNull ProgramList.Filter filter) {
+ static @NonNull ProgramFilter programFilterToHal(@Nullable ProgramList.Filter filter) {
+ if (filter == null) filter = new ProgramList.Filter();
+
ProgramFilter hwFilter = new ProgramFilter();
filter.getIdentifierTypes().stream().forEachOrdered(hwFilter.identifierTypes::add);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c4b2b5e..c7ae1f4 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -695,6 +695,27 @@
}
}
+ private void setSaturationLevelInternal(float level) {
+ if (level < 0 || level > 1) {
+ throw new IllegalArgumentException("Saturation level must be between 0 and 1");
+ }
+ float[] matrix = (level == 1.0f ? null : computeSaturationMatrix(level));
+ DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
+ dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_SATURATION, matrix);
+ }
+
+ private static float[] computeSaturationMatrix(float saturation) {
+ float desaturation = 1.0f - saturation;
+ float[] luminance = {0.231f * desaturation, 0.715f * desaturation, 0.072f * desaturation};
+ float[] matrix = {
+ luminance[0] + saturation, luminance[0], luminance[0], 0,
+ luminance[1], luminance[1] + saturation, luminance[1], 0,
+ luminance[2], luminance[2], luminance[2] + saturation, 0,
+ 0, 0, 0, 1
+ };
+ return matrix;
+ }
+
private int createVirtualDisplayInternal(IVirtualDisplayCallback callback,
IMediaProjection projection, int callingUid, String packageName, String name, int width,
int height, int densityDpi, Surface surface, int flags, String uniqueId) {
@@ -1687,6 +1708,19 @@
}
@Override // Binder call
+ public void setSaturationLevel(float level) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_SATURATION,
+ "Permission required to set display saturation level");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setSaturationLevelInternal(level);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public int createVirtualDisplay(IVirtualDisplayCallback callback,
IMediaProjection projection, String packageName, String name,
int width, int height, int densityDpi, Surface surface, int flags,
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index 000fcf3..a94f049 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -17,7 +17,6 @@
package com.android.server.display;
import android.app.ActivityManager;
-import android.app.IActivityManager;
import android.opengl.Matrix;
import android.os.IBinder;
import android.os.Parcel;
@@ -28,7 +27,6 @@
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-
import com.android.internal.app.ColorDisplayController;
import java.util.Arrays;
@@ -46,6 +44,10 @@
*/
public static final int LEVEL_COLOR_MATRIX_NIGHT_DISPLAY = 100;
/**
+ * Color transform level used to adjust the color saturation of the display.
+ */
+ public static final int LEVEL_COLOR_MATRIX_SATURATION = 150;
+ /**
* Color transform level used by A11y services to make the display monochromatic.
*/
public static final int LEVEL_COLOR_MATRIX_GRAYSCALE = 200;
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 3374b30..c445f73 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -2576,9 +2576,9 @@
// register for connectivity change events, this is equivalent to the deprecated way of
// registering for CONNECTIVITY_ACTION broadcasts
NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
- networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
- networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH);
+ networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ networkRequestBuilder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
NetworkRequest networkRequest = networkRequestBuilder.build();
mConnMgr.registerNetworkCallback(networkRequest, mNetworkConnectivityCallback);
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 30125f8..77d7c3c 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -31,7 +31,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.os.Binder;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
@@ -100,7 +99,6 @@
private final RecoverableKeyGenerator mRecoverableKeyGenerator;
private final RecoverySnapshotStorage mSnapshotStorage;
private final PlatformKeyManager mPlatformKeyManager;
- private final KeyStore mKeyStore;
private final ApplicationKeyStorage mApplicationKeyStorage;
/**
@@ -126,11 +124,10 @@
mInstance = new RecoverableKeyStoreManager(
context.getApplicationContext(),
- keystore,
db,
new RecoverySessionStorage(),
Executors.newSingleThreadExecutor(),
- new RecoverySnapshotStorage(),
+ RecoverySnapshotStorage.newInstance(),
new RecoverySnapshotListenersStorage(),
platformKeyManager,
applicationKeyStorage);
@@ -141,7 +138,6 @@
@VisibleForTesting
RecoverableKeyStoreManager(
Context context,
- KeyStore keystore,
RecoverableKeyStoreDb recoverableKeyStoreDb,
RecoverySessionStorage recoverySessionStorage,
ExecutorService executorService,
@@ -150,7 +146,6 @@
PlatformKeyManager platformKeyManager,
ApplicationKeyStorage applicationKeyStorage) {
mContext = context;
- mKeyStore = keystore;
mDatabase = recoverableKeyStoreDb;
mRecoverySessionStorage = recoverySessionStorage;
mExecutorService = executorService;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
new file mode 100644
index 0000000..f789155
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2018 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.locksettings.recoverablekeystore.serialization;
+
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERTIFICATE_FACTORY_TYPE;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
+
+import static com.android.server.locksettings.recoverablekeystore.serialization
+ .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_SNAPSHOT;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_DERIVATION_PARAMS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_MATERIAL;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_LOCK_SCREEN_UI_TYPE;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MAX_ATTEMPTS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MEMORY_DIFFICULTY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SALT;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SERVER_PARAMS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SNAPSHOT_VERSION;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_TRUSTED_HARDWARE_CERT_PATH;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_USER_SECRET_TYPE;
+
+import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+import android.util.Base64;
+import android.util.Xml;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Deserializes a {@link android.security.keystore.recovery.KeyChainSnapshot} instance from XML.
+ */
+public class KeyChainSnapshotDeserializer {
+
+ /**
+ * Deserializes a {@link KeyChainSnapshot} instance from the XML in the {@code inputStream}.
+ *
+ * @throws IOException if there is an IO error reading from the stream.
+ * @throws KeyChainSnapshotParserException if the XML does not conform to the expected XML for
+ * a snapshot.
+ */
+ public static KeyChainSnapshot deserialize(InputStream inputStream)
+ throws KeyChainSnapshotParserException, IOException {
+ try {
+ return deserializeInternal(inputStream);
+ } catch (XmlPullParserException e) {
+ throw new KeyChainSnapshotParserException("Malformed KeyChainSnapshot XML", e);
+ }
+ }
+
+ private static KeyChainSnapshot deserializeInternal(InputStream inputStream) throws IOException,
+ XmlPullParserException, KeyChainSnapshotParserException {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(inputStream, OUTPUT_ENCODING);
+
+ parser.nextTag();
+ parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
+
+ KeyChainSnapshot.Builder builder = new KeyChainSnapshot.Builder();
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+
+ switch (name) {
+ case TAG_SNAPSHOT_VERSION:
+ builder.setSnapshotVersion(readIntTag(parser, TAG_SNAPSHOT_VERSION));
+ break;
+
+ case TAG_RECOVERY_KEY_MATERIAL:
+ builder.setEncryptedRecoveryKeyBlob(
+ readBlobTag(parser, TAG_RECOVERY_KEY_MATERIAL));
+ break;
+
+ case TAG_COUNTER_ID:
+ builder.setCounterId(readLongTag(parser, TAG_COUNTER_ID));
+ break;
+
+ case TAG_SERVER_PARAMS:
+ builder.setServerParams(readBlobTag(parser, TAG_SERVER_PARAMS));
+ break;
+
+ case TAG_MAX_ATTEMPTS:
+ builder.setMaxAttempts(readIntTag(parser, TAG_MAX_ATTEMPTS));
+ break;
+
+ case TAG_TRUSTED_HARDWARE_CERT_PATH:
+ try {
+ builder.setTrustedHardwareCertPath(
+ readCertPathTag(parser, TAG_TRUSTED_HARDWARE_CERT_PATH));
+ } catch (CertificateException e) {
+ throw new KeyChainSnapshotParserException(
+ "Could not set trustedHardwareCertPath", e);
+ }
+ break;
+
+ case TAG_BACKEND_PUBLIC_KEY:
+ builder.setTrustedHardwarePublicKey(
+ readBlobTag(parser, TAG_BACKEND_PUBLIC_KEY));
+ break;
+
+ case TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST:
+ builder.setKeyChainProtectionParams(readKeyChainProtectionParamsList(parser));
+ break;
+
+ case TAG_APPLICATION_KEYS:
+ builder.setWrappedApplicationKeys(readWrappedApplicationKeys(parser));
+ break;
+
+ default:
+ throw new KeyChainSnapshotParserException(String.format(
+ Locale.US, "Unexpected tag %s in keyChainSnapshot", name));
+ }
+ }
+
+ parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
+ try {
+ return builder.build();
+ } catch (NullPointerException e) {
+ throw new KeyChainSnapshotParserException("Failed to build KeyChainSnapshot", e);
+ }
+ }
+
+ private static List<WrappedApplicationKey> readWrappedApplicationKeys(XmlPullParser parser)
+ throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+ parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEYS);
+ ArrayList<WrappedApplicationKey> keys = new ArrayList<>();
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+ keys.add(readWrappedApplicationKey(parser));
+ }
+ parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_APPLICATION_KEYS);
+ return keys;
+ }
+
+ private static WrappedApplicationKey readWrappedApplicationKey(XmlPullParser parser)
+ throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+ parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEY);
+ WrappedApplicationKey.Builder builder = new WrappedApplicationKey.Builder();
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+
+ switch (name) {
+ case TAG_ALIAS:
+ builder.setAlias(readStringTag(parser, TAG_ALIAS));
+ break;
+
+ case TAG_KEY_MATERIAL:
+ builder.setEncryptedKeyMaterial(readBlobTag(parser, TAG_KEY_MATERIAL));
+ break;
+
+ default:
+ throw new KeyChainSnapshotParserException(String.format(
+ Locale.US, "Unexpected tag %s in wrappedApplicationKey", name));
+ }
+ }
+ parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_APPLICATION_KEY);
+
+ try {
+ return builder.build();
+ } catch (NullPointerException e) {
+ throw new KeyChainSnapshotParserException("Failed to build WrappedApplicationKey", e);
+ }
+ }
+
+ private static List<KeyChainProtectionParams> readKeyChainProtectionParamsList(
+ XmlPullParser parser) throws IOException, XmlPullParserException,
+ KeyChainSnapshotParserException {
+ parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
+
+ ArrayList<KeyChainProtectionParams> keyChainProtectionParamsList = new ArrayList<>();
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+ keyChainProtectionParamsList.add(readKeyChainProtectionParams(parser));
+ }
+
+ parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
+ return keyChainProtectionParamsList;
+ }
+
+ private static KeyChainProtectionParams readKeyChainProtectionParams(XmlPullParser parser)
+ throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+ parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
+
+ KeyChainProtectionParams.Builder builder = new KeyChainProtectionParams.Builder();
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+
+ switch (name) {
+ case TAG_LOCK_SCREEN_UI_TYPE:
+ builder.setLockScreenUiFormat(readIntTag(parser, TAG_LOCK_SCREEN_UI_TYPE));
+ break;
+
+ case TAG_USER_SECRET_TYPE:
+ builder.setUserSecretType(readIntTag(parser, TAG_USER_SECRET_TYPE));
+ break;
+
+ case TAG_KEY_DERIVATION_PARAMS:
+ builder.setKeyDerivationParams(readKeyDerivationParams(parser));
+ break;
+
+ default:
+ throw new KeyChainSnapshotParserException(String.format(
+ Locale.US,
+ "Unexpected tag %s in keyChainProtectionParams",
+ name));
+
+ }
+ }
+
+ parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
+
+ try {
+ return builder.build();
+ } catch (NullPointerException e) {
+ throw new KeyChainSnapshotParserException(
+ "Failed to build KeyChainProtectionParams", e);
+ }
+ }
+
+ private static KeyDerivationParams readKeyDerivationParams(XmlPullParser parser)
+ throws XmlPullParserException, IOException, KeyChainSnapshotParserException {
+ parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
+
+ int memoryDifficulty = -1;
+ int algorithm = -1;
+ byte[] salt = null;
+
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+
+ switch (name) {
+ case TAG_MEMORY_DIFFICULTY:
+ memoryDifficulty = readIntTag(parser, TAG_MEMORY_DIFFICULTY);
+ break;
+
+ case TAG_ALGORITHM:
+ algorithm = readIntTag(parser, TAG_ALGORITHM);
+ break;
+
+ case TAG_SALT:
+ salt = readBlobTag(parser, TAG_SALT);
+ break;
+
+ default:
+ throw new KeyChainSnapshotParserException(
+ String.format(
+ Locale.US,
+ "Unexpected tag %s in keyDerivationParams",
+ name));
+ }
+ }
+
+ if (salt == null) {
+ throw new KeyChainSnapshotParserException("salt was not set in keyDerivationParams");
+ }
+
+ KeyDerivationParams keyDerivationParams = null;
+
+ switch (algorithm) {
+ case KeyDerivationParams.ALGORITHM_SHA256:
+ keyDerivationParams = KeyDerivationParams.createSha256Params(salt);
+ break;
+
+ case KeyDerivationParams.ALGORITHM_SCRYPT:
+ keyDerivationParams = KeyDerivationParams.createScryptParams(
+ salt, memoryDifficulty);
+ break;
+
+ default:
+ throw new KeyChainSnapshotParserException(
+ "Unknown algorithm in keyDerivationParams");
+ }
+
+ parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
+ return keyDerivationParams;
+ }
+
+ private static int readIntTag(XmlPullParser parser, String tagName)
+ throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+ parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
+ String text = readText(parser);
+ parser.require(XmlPullParser.END_TAG, NAMESPACE, tagName);
+ try {
+ return Integer.valueOf(text);
+ } catch (NumberFormatException e) {
+ throw new KeyChainSnapshotParserException(
+ String.format(
+ Locale.US, "%s expected int but got '%s'", tagName, text), e);
+ }
+ }
+
+ private static long readLongTag(XmlPullParser parser, String tagName)
+ throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+ parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
+ String text = readText(parser);
+ parser.require(XmlPullParser.END_TAG, NAMESPACE, tagName);
+ try {
+ return Long.valueOf(text);
+ } catch (NumberFormatException e) {
+ throw new KeyChainSnapshotParserException(
+ String.format(
+ Locale.US, "%s expected long but got '%s'", tagName, text), e);
+ }
+ }
+
+ private static String readStringTag(XmlPullParser parser, String tagName)
+ throws IOException, XmlPullParserException {
+ parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
+ String text = readText(parser);
+ parser.require(XmlPullParser.END_TAG, NAMESPACE, tagName);
+ return text;
+ }
+
+ private static byte[] readBlobTag(XmlPullParser parser, String tagName)
+ throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+ parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
+ String text = readText(parser);
+ parser.require(XmlPullParser.END_TAG, NAMESPACE, tagName);
+
+ try {
+ return Base64.decode(text, /*flags=*/ Base64.DEFAULT);
+ } catch (IllegalArgumentException e) {
+ throw new KeyChainSnapshotParserException(
+ String.format(
+ Locale.US,
+ "%s expected base64 encoded bytes but got '%s'",
+ tagName, text), e);
+ }
+ }
+
+ private static CertPath readCertPathTag(XmlPullParser parser, String tagName)
+ throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+ byte[] bytes = readBlobTag(parser, tagName);
+ try {
+ return CertificateFactory.getInstance(CERTIFICATE_FACTORY_TYPE)
+ .generateCertPath(new ByteArrayInputStream(bytes));
+ } catch (CertificateException e) {
+ throw new KeyChainSnapshotParserException("Could not parse CertPath in tag " + tagName,
+ e);
+ }
+ }
+
+ private static String readText(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ String result = "";
+ if (parser.next() == XmlPullParser.TEXT) {
+ result = parser.getText();
+ parser.nextTag();
+ }
+ return result;
+ }
+
+ // Statics only
+ private KeyChainSnapshotDeserializer() {}
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotParserException.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotParserException.java
new file mode 100644
index 0000000..a3208af
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotParserException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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.locksettings.recoverablekeystore.serialization;
+
+/**
+ * Error thrown when parsing invalid XML, while trying to read a
+ * {@link android.security.keystore.recovery.KeyChainSnapshot}.
+ */
+public class KeyChainSnapshotParserException extends Exception {
+
+ public KeyChainSnapshotParserException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public KeyChainSnapshotParserException(String message) {
+ super(message);
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
new file mode 100644
index 0000000..ff30ecd
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 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.locksettings.recoverablekeystore.serialization;
+
+/**
+ * Describes the XML schema of the {@link android.security.keystore.recovery.KeyChainSnapshot} file.
+ */
+class KeyChainSnapshotSchema {
+ static final String NAMESPACE = null;
+
+ static final String OUTPUT_ENCODING = "UTF-8";
+
+ static final String CERTIFICATE_FACTORY_TYPE = "X.509";
+ static final String CERT_PATH_ENCODING = "PkiPath";
+
+ static final String TAG_KEY_CHAIN_SNAPSHOT = "keyChainSnapshot";
+
+ static final String TAG_SNAPSHOT_VERSION = "snapshotVersion";
+ static final String TAG_COUNTER_ID = "counterId";
+ static final String TAG_MAX_ATTEMPTS = "maxAttempts";
+ static final String TAG_RECOVERY_KEY_MATERIAL = "recoveryKeyMaterial";
+ static final String TAG_SERVER_PARAMS = "serverParams";
+ static final String TAG_TRUSTED_HARDWARE_CERT_PATH = "thmCertPath";
+ static final String TAG_BACKEND_PUBLIC_KEY = "backendPublicKey";
+
+ static final String TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST =
+ "keyChainProtectionParamsList";
+ static final String TAG_KEY_CHAIN_PROTECTION_PARAMS = "keyChainProtectionParams";
+ static final String TAG_USER_SECRET_TYPE = "userSecretType";
+ static final String TAG_LOCK_SCREEN_UI_TYPE = "lockScreenUiType";
+
+ static final String TAG_KEY_DERIVATION_PARAMS = "keyDerivationParams";
+ static final String TAG_ALGORITHM = "algorithm";
+ static final String TAG_MEMORY_DIFFICULTY = "memoryDifficulty";
+ static final String TAG_SALT = "salt";
+
+ static final String TAG_APPLICATION_KEYS = "applicationKeysList";
+ static final String TAG_APPLICATION_KEY = "applicationKey";
+ static final String TAG_ALIAS = "alias";
+ static final String TAG_KEY_MATERIAL = "keyMaterial";
+
+ // Statics only
+ private KeyChainSnapshotSchema() {}
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
new file mode 100644
index 0000000..17a16bf
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2018 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.locksettings.recoverablekeystore.serialization;
+
+
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERT_PATH_ENCODING;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
+
+import static com.android.server.locksettings.recoverablekeystore.serialization
+ .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_SNAPSHOT;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_DERIVATION_PARAMS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_MATERIAL;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_LOCK_SCREEN_UI_TYPE;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MAX_ATTEMPTS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MEMORY_DIFFICULTY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SALT;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SERVER_PARAMS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SNAPSHOT_VERSION;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_TRUSTED_HARDWARE_CERT_PATH;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_USER_SECRET_TYPE;
+
+import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+import android.util.Base64;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateEncodingException;
+import java.util.List;
+
+/**
+ * Serializes a {@link KeyChainSnapshot} instance to XML.
+ */
+public class KeyChainSnapshotSerializer {
+
+ /**
+ * Serializes {@code keyChainSnapshot} to XML, writing to {@code outputStream}.
+ *
+ * @throws IOException if there was an IO error writing to the stream.
+ * @throws CertificateEncodingException if the {@link CertPath} from
+ * {@link KeyChainSnapshot#getTrustedHardwareCertPath()} is not encoded correctly.
+ */
+ public static void serialize(KeyChainSnapshot keyChainSnapshot, OutputStream outputStream)
+ throws IOException, CertificateEncodingException {
+ XmlSerializer xmlSerializer = Xml.newSerializer();
+ xmlSerializer.setOutput(outputStream, OUTPUT_ENCODING);
+ xmlSerializer.startDocument(
+ /*encoding=*/ null,
+ /*standalone=*/ null);
+ xmlSerializer.startTag(NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
+ writeKeyChainSnapshotProperties(xmlSerializer, keyChainSnapshot);
+ writeKeyChainProtectionParams(xmlSerializer,
+ keyChainSnapshot.getKeyChainProtectionParams());
+ writeApplicationKeys(xmlSerializer,
+ keyChainSnapshot.getWrappedApplicationKeys());
+ xmlSerializer.endTag(NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
+ xmlSerializer.endDocument();
+ }
+
+ private static void writeApplicationKeys(
+ XmlSerializer xmlSerializer, List<WrappedApplicationKey> wrappedApplicationKeys)
+ throws IOException {
+ xmlSerializer.startTag(NAMESPACE, TAG_APPLICATION_KEYS);
+ for (WrappedApplicationKey key : wrappedApplicationKeys) {
+ xmlSerializer.startTag(NAMESPACE, TAG_APPLICATION_KEY);
+ writeApplicationKeyProperties(xmlSerializer, key);
+ xmlSerializer.endTag(NAMESPACE, TAG_APPLICATION_KEY);
+ }
+ xmlSerializer.endTag(NAMESPACE, TAG_APPLICATION_KEYS);
+ }
+
+ private static void writeApplicationKeyProperties(
+ XmlSerializer xmlSerializer, WrappedApplicationKey applicationKey) throws IOException {
+ writePropertyTag(xmlSerializer, TAG_ALIAS, applicationKey.getAlias());
+ writePropertyTag(xmlSerializer, TAG_KEY_MATERIAL, applicationKey.getEncryptedKeyMaterial());
+ }
+
+ private static void writeKeyChainProtectionParams(
+ XmlSerializer xmlSerializer,
+ List<KeyChainProtectionParams> keyChainProtectionParamsList) throws IOException {
+ xmlSerializer.startTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
+ for (KeyChainProtectionParams keyChainProtectionParams : keyChainProtectionParamsList) {
+ xmlSerializer.startTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
+ writeKeyChainProtectionParamsProperties(xmlSerializer, keyChainProtectionParams);
+ xmlSerializer.endTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
+ }
+ xmlSerializer.endTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
+ }
+
+ private static void writeKeyChainProtectionParamsProperties(
+ XmlSerializer xmlSerializer, KeyChainProtectionParams keyChainProtectionParams)
+ throws IOException {
+ writePropertyTag(xmlSerializer, TAG_USER_SECRET_TYPE,
+ keyChainProtectionParams.getUserSecretType());
+ writePropertyTag(xmlSerializer, TAG_LOCK_SCREEN_UI_TYPE,
+ keyChainProtectionParams.getLockScreenUiFormat());
+
+ // NOTE: Do not serialize the 'secret' field. It should never be set anyway for snapshots
+ // we generate.
+
+ writeKeyDerivationParams(xmlSerializer, keyChainProtectionParams.getKeyDerivationParams());
+ }
+
+ private static void writeKeyDerivationParams(
+ XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
+ throws IOException {
+ xmlSerializer.startTag(NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
+ writeKeyDerivationParamsProperties(
+ xmlSerializer, keyDerivationParams);
+ xmlSerializer.endTag(NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
+ }
+
+ private static void writeKeyDerivationParamsProperties(
+ XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
+ throws IOException {
+ writePropertyTag(xmlSerializer, TAG_ALGORITHM, keyDerivationParams.getAlgorithm());
+ writePropertyTag(xmlSerializer, TAG_SALT, keyDerivationParams.getSalt());
+ writePropertyTag(xmlSerializer, TAG_MEMORY_DIFFICULTY,
+ keyDerivationParams.getMemoryDifficulty());
+ }
+
+ private static void writeKeyChainSnapshotProperties(
+ XmlSerializer xmlSerializer, KeyChainSnapshot keyChainSnapshot)
+ throws IOException, CertificateEncodingException {
+
+ writePropertyTag(xmlSerializer, TAG_SNAPSHOT_VERSION,
+ keyChainSnapshot.getSnapshotVersion());
+ writePropertyTag(xmlSerializer, TAG_MAX_ATTEMPTS, keyChainSnapshot.getMaxAttempts());
+ writePropertyTag(xmlSerializer, TAG_COUNTER_ID, keyChainSnapshot.getCounterId());
+ writePropertyTag(xmlSerializer, TAG_RECOVERY_KEY_MATERIAL,
+ keyChainSnapshot.getEncryptedRecoveryKeyBlob());
+ writePropertyTag(xmlSerializer, TAG_SERVER_PARAMS, keyChainSnapshot.getServerParams());
+ writePropertyTag(xmlSerializer, TAG_TRUSTED_HARDWARE_CERT_PATH,
+ keyChainSnapshot.getTrustedHardwareCertPath());
+ if (keyChainSnapshot.getTrustedHardwarePublicKey() != null) {
+ writePropertyTag(xmlSerializer, TAG_BACKEND_PUBLIC_KEY,
+ keyChainSnapshot.getTrustedHardwarePublicKey());
+ }
+ }
+
+ private static void writePropertyTag(
+ XmlSerializer xmlSerializer, String propertyName, long propertyValue)
+ throws IOException {
+ xmlSerializer.startTag(NAMESPACE, propertyName);
+ xmlSerializer.text(Long.toString(propertyValue));
+ xmlSerializer.endTag(NAMESPACE, propertyName);
+ }
+
+ private static void writePropertyTag(
+ XmlSerializer xmlSerializer, String propertyName, String propertyValue)
+ throws IOException {
+ xmlSerializer.startTag(NAMESPACE, propertyName);
+ xmlSerializer.text(propertyValue);
+ xmlSerializer.endTag(NAMESPACE, propertyName);
+ }
+
+ private static void writePropertyTag(
+ XmlSerializer xmlSerializer, String propertyName, byte[] propertyValue)
+ throws IOException {
+ xmlSerializer.startTag(NAMESPACE, propertyName);
+ xmlSerializer.text(Base64.encodeToString(propertyValue, /*flags=*/ Base64.DEFAULT));
+ xmlSerializer.endTag(NAMESPACE, propertyName);
+ }
+
+ private static void writePropertyTag(
+ XmlSerializer xmlSerializer, String propertyName, CertPath certPath)
+ throws IOException, CertificateEncodingException {
+ writePropertyTag(xmlSerializer, propertyName, certPath.getEncoded(CERT_PATH_ENCODING));
+ }
+
+ // Statics only
+ private KeyChainSnapshotSerializer() {}
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index 1eff2d4..7ee809a 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -70,121 +70,6 @@
}
/**
- * Table holding encrypted snapshots of the recoverable key store.
- */
- static class SnapshotsEntry implements BaseColumns {
- static final String TABLE_NAME = "snapshots";
-
- /**
- * The version number of the snapshot.
- */
- static final String COLUMN_NAME_VERSION = "version";
-
- /**
- * The ID of the user whose keystore was snapshotted.
- */
- static final String COLUMN_NAME_USER_ID = "user_id";
-
- /**
- * The UID of the app that owns the snapshot (i.e., the recovery agent).
- */
- static final String COLUMN_NAME_UID = "uid";
-
- /**
- * The maximum number of attempts allowed to attempt to decrypt the recovery key.
- */
- static final String COLUMN_NAME_MAX_ATTEMPTS = "max_attempts";
-
- /**
- * The ID of the counter in the trusted hardware module.
- */
- static final String COLUMN_NAME_COUNTER_ID = "counter_id";
-
- /**
- * Server parameters used to help identify the device (during recovery).
- */
- static final String SERVER_PARAMS = "server_params";
-
- /**
- * The public key of the trusted hardware module. This key has been used to encrypt the
- * snapshot, to ensure that it can only be read by the trusted module.
- */
- static final String TRUSTED_HARDWARE_PUBLIC_KEY = "thm_public_key";
-
- /**
- * {@link java.security.cert.CertPath} signing the trusted hardware module to whose public
- * key this snapshot is encrypted.
- */
- static final String CERT_PATH = "cert_path";
-
- /**
- * The recovery key, encrypted with the user's lock screen and the trusted hardware module's
- * public key.
- */
- static final String ENCRYPTED_RECOVERY_KEY = "encrypted_recovery_key";
- }
-
- /**
- * Table holding encrypted keys belonging to a particular snapshot.
- */
- static class SnapshotKeysEntry implements BaseColumns {
- static final String TABLE_NAME = "snapshot_keys";
-
- /**
- * ID of the associated snapshot entry in {@link SnapshotsEntry}.
- */
- static final String COLUMN_NAME_SNAPSHOT_ID = "snapshot_id";
-
- /**
- * Alias of the key.
- */
- static final String COLUMN_NAME_ALIAS = "alias";
-
- /**
- * Key material, encrypted with the recovery key from the snapshot.
- */
- static final String COLUMN_NAME_ENCRYPTED_BYTES = "encrypted_key_bytes";
- }
-
- /**
- * A layer of protection associated with a snapshot.
- */
- static class SnapshotProtectionParams implements BaseColumns {
- static final String TABLE_NAME = "snapshot_protection_params";
-
- /**
- * ID of the associated snapshot entry in {@link SnapshotsEntry}.
- */
- static final String COLUMN_NAME_SNAPSHOT_ID = "snapshot_id";
-
- /**
- * Type of secret used to generate recovery key. One of
- * {@link android.security.keystore.recovery.KeyChainProtectionParams#TYPE_LOCKSCREEN} or
- */
- static final String COLUMN_NAME_SECRET_TYPE = "secret_type";
-
- /**
- * If a lock screen, the type of UI used. One of
- * {@link android.security.keystore.recovery.KeyChainProtectionParams#UI_FORMAT_PATTERN},
- * {@link android.security.keystore.recovery.KeyChainProtectionParams#UI_FORMAT_PIN}, or
- * {@link android.security.keystore.recovery.KeyChainProtectionParams#UI_FORMAT_PASSWORD}.
- */
- static final String COLUMN_NAME_LOCKSCREEN_UI_TYPE = "lock_screen_ui_type";
-
- /**
- * The algorithm used to derive cryptographic material from the key and salt. One of
- * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_SHA256} or
- * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_SCRYPT}.
- */
- static final String COLUMN_NAME_KEY_DERIVATION_ALGORITHM = "key_derivation_algorithm";
-
- /**
- * The salt used along with the secret to generate cryptographic material.
- */
- static final String COLUMN_NAME_KEY_DERIVATION_SALT = "key_derivation_salt";
- }
-
- /**
* Recoverable KeyStore metadata for a specific user profile.
*/
static class UserMetadataEntry implements BaseColumns {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
index 3f93cc6..c02b103 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
@@ -17,13 +17,28 @@
package com.android.server.locksettings.recoverablekeystore.storage;
import android.annotation.Nullable;
+import android.os.Environment;
import android.security.keystore.recovery.KeyChainSnapshot;
+import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.recoverablekeystore.serialization
+ .KeyChainSnapshotDeserializer;
+import com.android.server.locksettings.recoverablekeystore.serialization
+ .KeyChainSnapshotParserException;
+import com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.util.Locale;
/**
- * In-memory storage for recovery snapshots.
+ * Storage for recovery snapshots. Stores snapshots in memory, backed by disk storage.
*
* <p>Recovery snapshots are generated after a successful screen unlock. They are only generated if
* the recoverable keystore has been mutated since the previous snapshot. This class stores only the
@@ -33,14 +48,46 @@
* {@link com.android.server.locksettings.recoverablekeystore.KeySyncTask} thread.
*/
public class RecoverySnapshotStorage {
+
+ private static final String TAG = "RecoverySnapshotStorage";
+
+ private static final String ROOT_PATH = "system";
+ private static final String STORAGE_PATH = "recoverablekeystore/snapshots/";
+
@GuardedBy("this")
private final SparseArray<KeyChainSnapshot> mSnapshotByUid = new SparseArray<>();
+ private final File rootDirectory;
+
+ /**
+ * A new instance, storing snapshots in /data/system/recoverablekeystore/snapshots.
+ *
+ * <p>NOTE: calling this multiple times DOES NOT return the same instance, so will NOT be backed
+ * by the same in-memory store.
+ */
+ public static RecoverySnapshotStorage newInstance() {
+ return new RecoverySnapshotStorage(
+ new File(Environment.getDataDirectory(), ROOT_PATH));
+ }
+
+ @VisibleForTesting
+ public RecoverySnapshotStorage(File rootDirectory) {
+ this.rootDirectory = rootDirectory;
+ }
+
/**
* Sets the latest {@code snapshot} for the recovery agent {@code uid}.
*/
public synchronized void put(int uid, KeyChainSnapshot snapshot) {
mSnapshotByUid.put(uid, snapshot);
+
+ try {
+ writeToDisk(uid, snapshot);
+ } catch (IOException | CertificateEncodingException e) {
+ Log.e(TAG,
+ String.format(Locale.US, "Error persisting snapshot for %d to disk", uid),
+ e);
+ }
}
/**
@@ -48,7 +95,17 @@
*/
@Nullable
public synchronized KeyChainSnapshot get(int uid) {
- return mSnapshotByUid.get(uid);
+ KeyChainSnapshot snapshot = mSnapshotByUid.get(uid);
+ if (snapshot != null) {
+ return snapshot;
+ }
+
+ try {
+ return readFromDisk(uid);
+ } catch (IOException | KeyChainSnapshotParserException e) {
+ Log.e(TAG, String.format(Locale.US, "Error reading snapshot for %d from disk", uid), e);
+ return null;
+ }
}
/**
@@ -56,5 +113,66 @@
*/
public synchronized void remove(int uid) {
mSnapshotByUid.remove(uid);
+ getSnapshotFile(uid).delete();
+ }
+
+ /**
+ * Writes the snapshot for recovery agent {@code uid} to disk.
+ *
+ * @throws IOException if an IO error occurs writing to disk.
+ */
+ private void writeToDisk(int uid, KeyChainSnapshot snapshot)
+ throws IOException, CertificateEncodingException {
+ File snapshotFile = getSnapshotFile(uid);
+
+ try (
+ FileOutputStream fileOutputStream = new FileOutputStream(snapshotFile)
+ ) {
+ KeyChainSnapshotSerializer.serialize(snapshot, fileOutputStream);
+ } catch (IOException | CertificateEncodingException e) {
+ // If we fail to write the latest snapshot, we should delete any older snapshot that
+ // happens to be around. Otherwise snapshot syncs might end up going 'back in time'.
+ snapshotFile.delete();
+ throw e;
+ }
+ }
+
+ /**
+ * Reads the last snapshot for recovery agent {@code uid} from disk.
+ *
+ * @return The snapshot, or null if none existed.
+ * @throws IOException if an IO error occurs reading from disk.
+ */
+ @Nullable
+ private KeyChainSnapshot readFromDisk(int uid)
+ throws IOException, KeyChainSnapshotParserException {
+ File snapshotFile = getSnapshotFile(uid);
+
+ try (
+ FileInputStream fileInputStream = new FileInputStream(snapshotFile)
+ ) {
+ return KeyChainSnapshotDeserializer.deserialize(fileInputStream);
+ } catch (IOException | KeyChainSnapshotParserException e) {
+ // If we fail to read the latest snapshot, we should delete it in case it is in some way
+ // corrupted. We can regenerate snapshots anyway.
+ snapshotFile.delete();
+ throw e;
+ }
+ }
+
+ private File getSnapshotFile(int uid) {
+ File folder = getStorageFolder();
+ String fileName = getSnapshotFileName(uid);
+ return new File(folder, fileName);
+ }
+
+ private String getSnapshotFileName(int uid) {
+ return String.format(Locale.US, "%d.xml", uid);
+ }
+
+ private File getStorageFolder() {
+ File folder = new File(rootDirectory, STORAGE_PATH);
+ folder.mkdirs();
+ return folder;
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 664d2f9..0d1644b 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -71,8 +71,6 @@
*/
private static final int OPTIMISTIC_VOLUME_TIMEOUT = 1000;
- private static final int UID_NOT_SET = -1;
-
private final MessageHandler mHandler;
private final int mOwnerPid;
@@ -117,9 +115,6 @@
private boolean mIsActive = false;
private boolean mDestroyed = false;
- private int mCallingUid = UID_NOT_SET;
- private String mCallingPackage;
-
public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
ISessionCallback cb, String tag, MediaSessionService service, Looper handlerLooper) {
mOwnerPid = ownerPid;
@@ -234,14 +229,14 @@
* {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE},
* {@link AudioManager#ADJUST_SAME}.
*
+ * @param packageName The package that made the original volume request.
+ * @param pid The pid that made the original volume request.
+ * @param uid The uid that made the original volume request.
* @param direction The direction to adjust volume in.
* @param flags Any of the flags from {@link AudioManager}.
- * @param packageName The package that made the original volume request.
- * @param uid The uid that made the original volume request.
* @param useSuggested True to use adjustSuggestedStreamVolume instead of
- * adjustStreamVolume.
*/
- public void adjustVolume(int direction, int flags, String packageName, int uid,
+ public void adjustVolume(String packageName, int pid, int uid, int direction, int flags,
boolean useSuggested) {
int previousFlagPlaySound = flags & AudioManager.FLAG_PLAY_SOUND;
if (isPlaybackActive() || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
@@ -263,7 +258,7 @@
Log.w(TAG, "Muting remote playback is not supported");
return;
}
- mSessionCb.adjustVolume(direction);
+ mSessionCb.adjustVolume(packageName, pid, uid, direction);
int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume);
mOptimisticVolume = volumeBefore + direction;
@@ -282,7 +277,7 @@
}
}
- public void setVolumeTo(int value, int flags, String packageName, int uid) {
+ public void setVolumeTo(String packageName, int pid, int uid, int value, int flags) {
if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
mAudioManagerInternal.setStreamVolumeForUid(stream, value, flags, packageName, uid);
@@ -292,7 +287,7 @@
return;
}
value = Math.max(0, Math.min(value, mMaxVolume));
- mSessionCb.setVolumeTo(value);
+ mSessionCb.setVolumeTo(packageName, pid, uid, value);
int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume);
mOptimisticVolume = Math.max(0, Math.min(value, mMaxVolume));
@@ -423,10 +418,9 @@
return mSessionCb.mCb;
}
- public void sendMediaButton(KeyEvent ke, int sequenceId,
- ResultReceiver cb, int uid, String packageName) {
- updateCallingPackage(uid, packageName);
- mSessionCb.sendMediaButton(ke, sequenceId, cb);
+ public void sendMediaButton(String packageName, int pid, int uid, KeyEvent ke, int sequenceId,
+ ResultReceiver cb) {
+ mSessionCb.sendMediaButton(packageName, pid, uid, ke, sequenceId, cb);
}
public void dump(PrintWriter pw, String prefix) {
@@ -703,22 +697,6 @@
return -1;
}
- private void updateCallingPackage() {
- updateCallingPackage(UID_NOT_SET, null);
- }
-
- private void updateCallingPackage(int uid, String packageName) {
- if (uid == UID_NOT_SET) {
- uid = Binder.getCallingUid();
- }
- synchronized (mLock) {
- if (mCallingUid == UID_NOT_SET || mCallingUid != uid) {
- mCallingUid = uid;
- mCallingPackage = packageName != null ? packageName : getPackageName(uid);
- }
- }
- }
-
private String getPackageName(int uid) {
Context context = mService.getContext();
if (context == null) {
@@ -920,11 +898,6 @@
mHandler.post(MessageHandler.MSG_UPDATE_VOLUME);
}
}
-
- @Override
- public String getCallingPackage() {
- return mCallingPackage;
- }
}
class SessionCb {
@@ -934,11 +907,12 @@
mCb = cb;
}
- public boolean sendMediaButton(KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
+ public boolean sendMediaButton(String packageName, int pid, int uid, KeyEvent keyEvent,
+ int sequenceId, ResultReceiver cb) {
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
try {
- mCb.onMediaButton(mediaButtonIntent, sequenceId, cb);
+ mCb.onMediaButton(packageName, pid, uid, mediaButtonIntent, sequenceId, cb);
return true;
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
@@ -946,169 +920,176 @@
return false;
}
- public void sendCommand(String command, Bundle args, ResultReceiver cb) {
+ public void sendCommand(String packageName, int pid, int uid, String command, Bundle args,
+ ResultReceiver cb) {
try {
- mCb.onCommand(command, args, cb);
+ mCb.onCommand(packageName, pid, uid, command, args, cb);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in sendCommand.", e);
}
}
- public void sendCustomAction(String action, Bundle args) {
+ public void sendCustomAction(String packageName, int pid, int uid, String action,
+ Bundle args) {
try {
- mCb.onCustomAction(action, args);
+ mCb.onCustomAction(packageName, pid, uid, action, args);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in sendCustomAction.", e);
}
}
- public void prepare() {
+ public void prepare(String packageName, int pid, int uid) {
try {
- mCb.onPrepare();
+ mCb.onPrepare(packageName, pid, uid);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in prepare.", e);
}
}
- public void prepareFromMediaId(String mediaId, Bundle extras) {
+ public void prepareFromMediaId(String packageName, int pid, int uid, String mediaId,
+ Bundle extras) {
try {
- mCb.onPrepareFromMediaId(mediaId, extras);
+ mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in prepareFromMediaId.", e);
}
}
- public void prepareFromSearch(String query, Bundle extras) {
+ public void prepareFromSearch(String packageName, int pid, int uid, String query,
+ Bundle extras) {
try {
- mCb.onPrepareFromSearch(query, extras);
+ mCb.onPrepareFromSearch(packageName, pid, uid, query, extras);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in prepareFromSearch.", e);
}
}
- public void prepareFromUri(Uri uri, Bundle extras) {
+ public void prepareFromUri(String packageName, int pid, int uid, Uri uri,
+ Bundle extras) {
try {
- mCb.onPrepareFromUri(uri, extras);
+ mCb.onPrepareFromUri(packageName, pid, uid, uri, extras);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in prepareFromUri.", e);
}
}
- public void play() {
+ public void play(String packageName, int pid, int uid) {
try {
- mCb.onPlay();
+ mCb.onPlay(packageName, pid, uid);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in play.", e);
}
}
- public void playFromMediaId(String mediaId, Bundle extras) {
+ public void playFromMediaId(String packageName, int pid, int uid, String mediaId,
+ Bundle extras) {
try {
- mCb.onPlayFromMediaId(mediaId, extras);
+ mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in playFromMediaId.", e);
}
}
- public void playFromSearch(String query, Bundle extras) {
+ public void playFromSearch(String packageName, int pid, int uid, String query,
+ Bundle extras) {
try {
- mCb.onPlayFromSearch(query, extras);
+ mCb.onPlayFromSearch(packageName, pid, uid, query, extras);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in playFromSearch.", e);
}
}
- public void playFromUri(Uri uri, Bundle extras) {
+ public void playFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
try {
- mCb.onPlayFromUri(uri, extras);
+ mCb.onPlayFromUri(packageName, pid, uid, uri, extras);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in playFromUri.", e);
}
}
- public void skipToTrack(long id) {
+ public void skipToTrack(String packageName, int pid, int uid, long id) {
try {
- mCb.onSkipToTrack(id);
+ mCb.onSkipToTrack(packageName, pid, uid, id);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in skipToTrack", e);
}
}
- public void pause() {
+ public void pause(String packageName, int pid, int uid) {
try {
- mCb.onPause();
+ mCb.onPause(packageName, pid, uid);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in pause.", e);
}
}
- public void stop() {
+ public void stop(String packageName, int pid, int uid) {
try {
- mCb.onStop();
+ mCb.onStop(packageName, pid, uid);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in stop.", e);
}
}
- public void next() {
+ public void next(String packageName, int pid, int uid) {
try {
- mCb.onNext();
+ mCb.onNext(packageName, pid, uid);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in next.", e);
}
}
- public void previous() {
+ public void previous(String packageName, int pid, int uid) {
try {
- mCb.onPrevious();
+ mCb.onPrevious(packageName, pid, uid);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in previous.", e);
}
}
- public void fastForward() {
+ public void fastForward(String packageName, int pid, int uid) {
try {
- mCb.onFastForward();
+ mCb.onFastForward(packageName, pid, uid);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in fastForward.", e);
}
}
- public void rewind() {
+ public void rewind(String packageName, int pid, int uid) {
try {
- mCb.onRewind();
+ mCb.onRewind(packageName, pid, uid);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in rewind.", e);
}
}
- public void seekTo(long pos) {
+ public void seekTo(String packageName, int pid, int uid, long pos) {
try {
- mCb.onSeekTo(pos);
+ mCb.onSeekTo(packageName, pid, uid, pos);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in seekTo.", e);
}
}
- public void rate(Rating rating) {
+ public void rate(String packageName, int pid, int uid, Rating rating) {
try {
- mCb.onRate(rating);
+ mCb.onRate(packageName, pid, uid, rating);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in rate.", e);
}
}
- public void adjustVolume(int direction) {
+ public void adjustVolume(String packageName, int pid, int uid, int direction) {
try {
- mCb.onAdjustVolume(direction);
+ mCb.onAdjustVolume(packageName, pid, uid, direction);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in adjustVolume.", e);
}
}
- public void setVolumeTo(int value) {
+ public void setVolumeTo(String packageName, int pid, int uid, int value) {
try {
- mCb.onSetVolumeTo(value);
+ mCb.onSetVolumeTo(packageName, pid, uid, value);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in setVolumeTo.", e);
}
@@ -1117,16 +1098,16 @@
class ControllerStub extends ISessionController.Stub {
@Override
- public void sendCommand(String command, Bundle args, ResultReceiver cb)
- throws RemoteException {
- updateCallingPackage();
- mSessionCb.sendCommand(command, args, cb);
+ public void sendCommand(String packageName, String command, Bundle args,
+ ResultReceiver cb) {
+ mSessionCb.sendCommand(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+ command, args, cb);
}
@Override
- public boolean sendMediaButton(KeyEvent mediaButtonIntent) {
- updateCallingPackage();
- return mSessionCb.sendMediaButton(mediaButtonIntent, 0, null);
+ public boolean sendMediaButton(String packageName, KeyEvent mediaButtonIntent) {
+ return mSessionCb.sendMediaButton(packageName, Binder.getCallingPid(),
+ Binder.getCallingUid(), mediaButtonIntent, 0, null);
}
@Override
@@ -1207,137 +1188,125 @@
}
@Override
- public void adjustVolume(int direction, int flags, String packageName) {
- updateCallingPackage();
+ public void adjustVolume(String packageName, int direction, int flags) {
+ int pid = Binder.getCallingPid();
int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- MediaSessionRecord.this.adjustVolume(direction, flags, packageName, uid, false);
+ MediaSessionRecord.this.adjustVolume(packageName, pid, uid, direction, flags,
+ false /* useSuggested */);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
- public void setVolumeTo(int value, int flags, String packageName) {
- updateCallingPackage();
+ public void setVolumeTo(String packageName, int value, int flags) {
+ int pid = Binder.getCallingPid();
int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- MediaSessionRecord.this.setVolumeTo(value, flags, packageName, uid);
+ MediaSessionRecord.this.setVolumeTo(packageName, pid, uid, value, flags);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
- public void prepare() throws RemoteException {
- updateCallingPackage();
- mSessionCb.prepare();
+ public void prepare(String packageName) {
+ mSessionCb.prepare(packageName, Binder.getCallingPid(), Binder.getCallingUid());
}
@Override
- public void prepareFromMediaId(String mediaId, Bundle extras)
- throws RemoteException {
- updateCallingPackage();
- mSessionCb.prepareFromMediaId(mediaId, extras);
+ public void prepareFromMediaId(String packageName, String mediaId, Bundle extras) {
+ mSessionCb.prepareFromMediaId(packageName, Binder.getCallingPid(),
+ Binder.getCallingUid(), mediaId, extras);
}
@Override
- public void prepareFromSearch(String query, Bundle extras) throws RemoteException {
- updateCallingPackage();
- mSessionCb.prepareFromSearch(query, extras);
+ public void prepareFromSearch(String packageName, String query, Bundle extras) {
+ mSessionCb.prepareFromSearch(packageName, Binder.getCallingPid(),
+ Binder.getCallingUid(), query, extras);
}
@Override
- public void prepareFromUri(Uri uri, Bundle extras) throws RemoteException {
- updateCallingPackage();
- mSessionCb.prepareFromUri(uri, extras);
+ public void prepareFromUri(String packageName, Uri uri, Bundle extras) {
+ mSessionCb.prepareFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+ uri, extras);
}
@Override
- public void play() throws RemoteException {
- updateCallingPackage();
- mSessionCb.play();
+ public void play(String packageName) {
+ mSessionCb.play(packageName, Binder.getCallingPid(), Binder.getCallingUid());
}
@Override
- public void playFromMediaId(String mediaId, Bundle extras) throws RemoteException {
- updateCallingPackage();
- mSessionCb.playFromMediaId(mediaId, extras);
+ public void playFromMediaId(String packageName, String mediaId, Bundle extras) {
+ mSessionCb.playFromMediaId(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+ mediaId, extras);
}
@Override
- public void playFromSearch(String query, Bundle extras) throws RemoteException {
- updateCallingPackage();
- mSessionCb.playFromSearch(query, extras);
+ public void playFromSearch(String packageName, String query, Bundle extras) {
+ mSessionCb.playFromSearch(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+ query, extras);
}
@Override
- public void playFromUri(Uri uri, Bundle extras) throws RemoteException {
- updateCallingPackage();
- mSessionCb.playFromUri(uri, extras);
+ public void playFromUri(String packageName, Uri uri, Bundle extras) {
+ mSessionCb.playFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+ uri, extras);
}
@Override
- public void skipToQueueItem(long id) {
- updateCallingPackage();
- mSessionCb.skipToTrack(id);
+ public void skipToQueueItem(String packageName, long id) {
+ mSessionCb.skipToTrack(packageName, Binder.getCallingPid(), Binder.getCallingUid(), id);
}
@Override
- public void pause() throws RemoteException {
- updateCallingPackage();
- mSessionCb.pause();
+ public void pause(String packageName) {
+ mSessionCb.pause(packageName, Binder.getCallingPid(), Binder.getCallingUid());
}
@Override
- public void stop() throws RemoteException {
- updateCallingPackage();
- mSessionCb.stop();
+ public void stop(String packageName) {
+ mSessionCb.stop(packageName, Binder.getCallingPid(), Binder.getCallingUid());
}
@Override
- public void next() throws RemoteException {
- updateCallingPackage();
- mSessionCb.next();
+ public void next(String packageName) {
+ mSessionCb.next(packageName, Binder.getCallingPid(), Binder.getCallingUid());
}
@Override
- public void previous() throws RemoteException {
- updateCallingPackage();
- mSessionCb.previous();
+ public void previous(String packageName) {
+ mSessionCb.previous(packageName, Binder.getCallingPid(), Binder.getCallingUid());
}
@Override
- public void fastForward() throws RemoteException {
- updateCallingPackage();
- mSessionCb.fastForward();
+ public void fastForward(String packageName) {
+ mSessionCb.fastForward(packageName, Binder.getCallingPid(), Binder.getCallingUid());
}
@Override
- public void rewind() throws RemoteException {
- updateCallingPackage();
- mSessionCb.rewind();
+ public void rewind(String packageName) {
+ mSessionCb.rewind(packageName, Binder.getCallingPid(), Binder.getCallingUid());
}
@Override
- public void seekTo(long pos) throws RemoteException {
- updateCallingPackage();
- mSessionCb.seekTo(pos);
+ public void seekTo(String packageName, long pos) {
+ mSessionCb.seekTo(packageName, Binder.getCallingPid(), Binder.getCallingUid(), pos);
}
@Override
- public void rate(Rating rating) throws RemoteException {
- updateCallingPackage();
- mSessionCb.rate(rating);
+ public void rate(String packageName, Rating rating) {
+ mSessionCb.rate(packageName, Binder.getCallingPid(), Binder.getCallingUid(), rating);
}
@Override
- public void sendCustomAction(String action, Bundle args)
- throws RemoteException {
- updateCallingPackage();
- mSessionCb.sendCustomAction(action, args);
+ public void sendCustomAction(String packageName, String action, Bundle args) {
+ mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+ action, args);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 0eb906d..6413ba9 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -1804,8 +1804,8 @@
}
});
} else {
- session.adjustVolume(direction, flags, getContext().getPackageName(),
- Process.SYSTEM_UID, true);
+ session.adjustVolume(getContext().getPackageName(), Process.myPid(),
+ Process.SYSTEM_UID, direction, flags, true);
}
}
@@ -1843,10 +1843,12 @@
mKeyEventReceiver.aquireWakeLockLocked();
}
// If we don't need a wakelock use -1 as the id so we won't release it later.
- session.sendMediaButton(keyEvent,
+ session.sendMediaButton(getContext().getPackageName(),
+ Process.myPid(),
+ Process.SYSTEM_UID,
+ keyEvent,
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
- mKeyEventReceiver, Process.SYSTEM_UID,
- getContext().getPackageName());
+ mKeyEventReceiver);
if (mCurrentFullUserRecord.mCallback != null) {
try {
mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 210857e..31b0461 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2149,7 +2149,8 @@
final NotificationChannel channel = channels.get(i);
Preconditions.checkNotNull(channel, "channel in list is null");
mRankingHelper.createNotificationChannel(pkg, uid, channel,
- true /* fromTargetApp */);
+ true /* fromTargetApp */, mConditionProviders.isPackageOrComponentAllowed(
+ pkg, UserHandle.getUserId(uid)));
mListeners.notifyNotificationChannelChanged(pkg,
UserHandle.getUserHandleForUid(uid),
mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
@@ -4691,11 +4692,12 @@
private boolean playSound(final NotificationRecord record, Uri soundUri) {
boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
- // do not play notifications if there is a user of exclusive audio focus
- // or the device is in vibrate mode
- if (!mAudioManager.isAudioFocusExclusive() && (mAudioManager.getRingerModeInternal()
- != AudioManager.RINGER_MODE_VIBRATE || mAudioManager.getStreamVolume(
- AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
+ // play notifications if there is no user of exclusive audio focus
+ // and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
+ // VIBRATE ringer mode)
+ if (!mAudioManager.isAudioFocusExclusive()
+ && (mAudioManager.getStreamVolume(
+ AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
final long identity = Binder.clearCallingIdentity();
try {
final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index b1b0bf2..af64683 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -38,7 +38,7 @@
ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
int uid, boolean includeDeleted, boolean includeNonGrouped);
void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
- boolean fromTargetApp);
+ boolean fromTargetApp, boolean hasDndAccess);
void updateNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromUser);
NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, int uid, String channelId);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index f163113..98d5c9a 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -569,7 +569,7 @@
@Override
public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
- boolean fromTargetApp) {
+ boolean fromTargetApp, boolean hasDndAccess) {
Preconditions.checkNotNull(pkg);
Preconditions.checkNotNull(channel);
Preconditions.checkNotNull(channel.getId());
@@ -610,8 +610,9 @@
existing.setImportance(channel.getImportance());
}
- // system apps can bypass dnd if the user hasn't changed any fields on the channel yet
- if (existing.getUserLockedFields() == 0 & isSystemApp) {
+ // system apps and dnd access apps can bypass dnd if the user hasn't changed any
+ // fields on the channel yet
+ if (existing.getUserLockedFields() == 0 && (isSystemApp || hasDndAccess)) {
existing.setBypassDnd(channel.canBypassDnd());
}
@@ -624,7 +625,7 @@
}
// Reset fields that apps aren't allowed to set.
- if (fromTargetApp && !isSystemApp) {
+ if (fromTargetApp && !(isSystemApp || hasDndAccess)) {
channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
}
if (fromTargetApp) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 61c6be7..9807342 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -449,13 +449,15 @@
pw.increaseIndent();
for (String isa : dexCodeInstructionSets) {
- String status = null;
try {
- status = DexFile.getDexFileStatus(path, isa);
+ String[] status = DexFile.getDexFileOptimizationStatus(path, isa);
+ String compilationStatus = status[0];
+ String compilationReason = status[1];
+ pw.println(isa + ": [status=" + compilationStatus
+ +"] reason=[" + compilationReason + "]");
} catch (IOException ioe) {
- status = "[Exception]: " + ioe.getMessage();
+ pw.println(isa + ": [Exception]: " + ioe.getMessage());
}
- pw.println(isa + ": " + status);
}
if (useInfo.isUsedByOtherApps(path)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 775fdaa8..b0b313b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -828,11 +828,9 @@
//
// OverlayManagerService will update each of them with a correct gid from its
// target package app id.
- synchronized (mInstallLock) {
- mInstaller.idmap(targetPath, overlayPackage.baseCodePath,
- UserHandle.getSharedAppGid(
- UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
- }
+ mInstaller.idmap(targetPath, overlayPackage.baseCodePath,
+ UserHandle.getSharedAppGid(
+ UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
if (overlayPathList == null) {
overlayPathList = new ArrayList<String>();
}
@@ -847,13 +845,15 @@
String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
List<PackageParser.Package> overlayPackages;
- synchronized (mPackages) {
- overlayPackages = getStaticOverlayPackages(
- mPackages.values(), targetPackageName);
+ synchronized (mInstallLock) {
+ synchronized (mPackages) {
+ overlayPackages = getStaticOverlayPackages(
+ mPackages.values(), targetPackageName);
+ }
+ // It is safe to keep overlayPackages without holding mPackages because static overlay
+ // packages can't be uninstalled or disabled.
+ return getStaticOverlayPaths(overlayPackages, targetPath);
}
- // It is safe to keep overlayPackages without holding mPackages because static overlay
- // packages can't be uninstalled or disabled.
- return getStaticOverlayPaths(overlayPackages, targetPath);
}
@Override public final String[] getOverlayApks(String targetPackageName) {
@@ -886,7 +886,9 @@
synchronized String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
// We can trust mOverlayPackages without holding mPackages because package uninstall
// can't happen while running parallel parsing.
- // Moreover holding mPackages on each parsing thread causes dead-lock.
+ // And we can call mInstaller inside getStaticOverlayPaths without holding mInstallLock
+ // because mInstallLock is held before running parallel parsing.
+ // Moreover holding mPackages or mInstallLock on each parsing thread causes dead-lock.
return mOverlayPackages == null ? null :
getStaticOverlayPaths(
getStaticOverlayPackages(mOverlayPackages, targetPackageName),
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2e6e348..fd02347 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1330,6 +1330,9 @@
mHandler.post(mHiddenNavPanic);
}
+ // Abort possibly stuck animations.
+ mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
+
// Latch power key state to detect screenshot chord.
if (interactive && !mScreenshotChordPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
@@ -4362,6 +4365,9 @@
* given the situation with the keyguard.
*/
void launchHomeFromHotKey(final boolean awakenFromDreams, final boolean respectKeyguard) {
+ // Abort possibly stuck animations.
+ mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
+
if (respectKeyguard) {
if (isKeyguardShowingAndNotOccluded()) {
// don't launch home if keyguard showing
@@ -7175,7 +7181,7 @@
}
@Override
- public int rotationForOrientationLw(int orientation, int lastRotation) {
+ public int rotationForOrientationLw(int orientation, int lastRotation, boolean defaultDisplay) {
if (false) {
Slog.v(TAG, "rotationForOrientationLw(orient="
+ orientation + ", last=" + lastRotation
@@ -7196,7 +7202,11 @@
}
final int preferredRotation;
- if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
+ if (!defaultDisplay) {
+ // For secondary displays we ignore things like displays sensors, docking mode and
+ // rotation lock, and always prefer a default rotation.
+ preferredRotation = Surface.ROTATION_0;
+ } else if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
// Ignore sensor when lid switch is open and rotation is forced.
preferredRotation = mLidOpenRotation;
} else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index ec0521d..0a6ae4e 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -407,7 +407,10 @@
/**
* Returns true if this window has been shown on screen at some time in
* the past. Must be called with the window manager lock held.
+ *
+ * @deprecated Use {@link #isDrawnLw} or any of the other drawn/visibility methods.
*/
+ @Deprecated
public boolean hasDrawnLw();
/**
@@ -649,6 +652,12 @@
return Integer.toString(lens);
}
}
+
+ /**
+ * Hint to window manager that the user has started a navigation action that should
+ * abort animations that have no timeout, in case they got stuck.
+ */
+ void triggerAnimationFailsafe();
}
/** Window has been added to the screen. */
@@ -1426,10 +1435,13 @@
* @param orientation An orientation constant, such as
* {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
* @param lastRotation The most recently used rotation.
+ * @param defaultDisplay Flag indicating whether the rotation is computed for the default
+ * display. Currently for all non-default displays sensors, docking mode,
+ * rotation lock and other factors are ignored.
* @return The surface rotation to use.
*/
public int rotationForOrientationLw(@ActivityInfo.ScreenOrientation int orientation,
- int lastRotation);
+ int lastRotation, boolean defaultDisplay);
/**
* Given an orientation constant and a rotation, returns true if the rotation
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index b3d28fc..d252a56 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -466,34 +466,32 @@
}
@Override // Binder call
- public void setPullingAlarms(long timestampMs, long intervalMs) {
- enforceCallingPermission();
- if (DEBUG)
- Slog.d(TAG, "Setting pulling alarm for " + timestampMs + " every " + intervalMs + "ms");
- final long callingToken = Binder.clearCallingIdentity();
- try {
- // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
- // only fire when it awakens.
- // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
- // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
- mAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, timestampMs, intervalMs,
- mPullingAlarmIntent);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
+ public void setPullingAlarm(long nextPullTimeMs) {
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG,
+ "Setting pulling alarm in about " + (nextPullTimeMs - SystemClock.elapsedRealtime()));
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
+ // only fire when it awakens.
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextPullTimeMs, mPullingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
}
@Override // Binder call
- public void cancelPullingAlarms() {
- enforceCallingPermission();
- if (DEBUG)
- Slog.d(TAG, "Cancelling pulling alarm");
- final long callingToken = Binder.clearCallingIdentity();
- try {
- mAlarmManager.cancel(mPullingAlarmIntent);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
+ public void cancelPullingAlarm() {
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG, "Cancelling pulling alarm");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ mAlarmManager.cancel(mPullingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
}
private void addNetworkStats(
@@ -1109,7 +1107,7 @@
mContext.unregisterReceiver(mUserUpdateReceiver);
mContext.unregisterReceiver(mShutdownEventReceiver);
cancelAnomalyAlarm();
- cancelPullingAlarms();
+ cancelPullingAlarm();
}
}
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index 9b787de..9177d25 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -37,6 +37,8 @@
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Bundle;
+
import com.android.internal.R;
import com.android.server.policy.IconUtilities;
@@ -109,6 +111,8 @@
final String message = context.getString(R.string.alert_windows_notification_message,
appName);
+ Bundle extras = new Bundle();
+ extras.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {mPackageName});
final Notification.Builder builder = new Notification.Builder(context, mNotificationTag)
.setOngoing(true)
.setContentTitle(
@@ -118,6 +122,7 @@
.setColor(context.getColor(R.color.system_notification_accent_color))
.setStyle(new Notification.BigTextStyle().bigText(message))
.setLocalOnly(true)
+ .addExtras(extras)
.setContentIntent(getContentIntent(context, mPackageName));
if (aInfo != null) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 56c9e51..f19c554 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1485,7 +1485,7 @@
if (w == null || winHint != null && w != winHint) {
return;
}
- final boolean surfaceReady = w.hasDrawnLw() // Regular case
+ final boolean surfaceReady = w.isDrawnLw() // Regular case
|| w.mWinAnimator.mSurfaceDestroyDeferred // The preserved surface is still ready.
|| w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface.
final boolean needsLetterbox = w.isLetterboxedAppWindow() && fillsParent() && surfaceReady;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2ffdbfd..c9ff9e3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -909,6 +909,7 @@
return mRotation;
}
+ @VisibleForTesting
void setRotation(int newRotation) {
mRotation = newRotation;
}
@@ -974,7 +975,11 @@
final int oldRotation = mRotation;
final int lastOrientation = mLastOrientation;
final boolean oldAltOrientation = mAltOrientation;
- int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation);
+ final int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation,
+ isDefaultDisplay);
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="
+ + mDisplayId + " based on lastOrientation=" + lastOrientation
+ + " and oldRotation=" + oldRotation);
boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
rotation);
@@ -1010,7 +1015,8 @@
final boolean altOrientation = !mService.mPolicy.rotationHasCompatibleMetricsLw(
lastOrientation, rotation);
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Selected orientation " + lastOrientation
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+ + " selected orientation " + lastOrientation
+ ", got rotation " + rotation + " which has "
+ (altOrientation ? "incompatible" : "compatible") + " metrics");
@@ -1019,7 +1025,8 @@
return false;
}
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Rotation changed to " + rotation
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+ + " rotation changed to " + rotation
+ (altOrientation ? " (alt)" : "") + " from " + oldRotation
+ (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
@@ -1647,8 +1654,8 @@
if (mService.mDisplayFrozen) {
if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "Display is frozen, return " + mLastWindowForcedOrientation);
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+ + " is frozen, return " + mLastWindowForcedOrientation);
// If the display is frozen, some activities may be in the middle of restarting, and
// thus have removed their old window. If the window has the flag to hide the lock
// screen, then the lock screen can re-appear and inflict its own orientation on us.
@@ -1660,8 +1667,8 @@
// window. We don't want to check the show when locked window directly though as
// things aren't stable while the display is frozen, for example the window could be
// momentarily unavailable due to activity relaunch.
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display is frozen while keyguard locked, "
- + "return " + mLastOrientation);
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+ + " is frozen while keyguard locked, return " + mLastOrientation);
return mLastOrientation;
}
} else {
@@ -3503,19 +3510,22 @@
// In a car, you cannot physically rotate the screen, so it doesn't make sense to
// allow anything but the default orientation.
if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "Forcing UNSPECIFIED orientation in car. Ignoring " + orientation);
+ "Forcing UNSPECIFIED orientation in car for display id=" + mDisplayId
+ + ". Ignoring " + orientation);
return SCREEN_ORIENTATION_UNSPECIFIED;
}
if (orientation != SCREEN_ORIENTATION_UNSET
&& orientation != SCREEN_ORIENTATION_BEHIND) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "App is requesting an orientation, return " + orientation);
+ "App is requesting an orientation, return " + orientation
+ + " for display id=" + mDisplayId);
return orientation;
}
if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "No app is requesting an orientation, return " + mLastOrientation);
+ "No app is requesting an orientation, return " + mLastOrientation
+ + " for display id=" + mDisplayId);
// The next app has not been requested to be visible, so we keep the current orientation
// to prevent freezing/unfreezing the display too early.
return mLastOrientation;
@@ -3708,7 +3718,8 @@
return SCREEN_ORIENTATION_UNSET;
}
}
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req);
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req
+ + " for display id=" + mDisplayId);
return (mLastWindowForcedOrientation = req);
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 1018848..7274aee 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -34,6 +34,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.ArraySet;
@@ -61,15 +62,17 @@
* window manager when the animation is completed. In addition, window manager may also notify the
* app if it requires the animation to be canceled at any time (ie. due to timeout, etc.)
*/
-public class RecentsAnimationController {
+public class RecentsAnimationController implements DeathRecipient {
private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentsAnimationController" : TAG_WM;
private static final boolean DEBUG = false;
+ private static final long FAILSAFE_DELAY = 1000;
private final WindowManagerService mService;
private final IRecentsAnimationRunner mRunner;
private final RecentsAnimationCallbacks mCallbacks;
private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
private final int mDisplayId;
+ private final Runnable mFailsafeRunnable = this::cancelAnimation;
// The recents component app token that is shown behind the visibile tasks
private AppWindowToken mHomeAppToken;
@@ -223,6 +226,13 @@
return;
}
+ try {
+ mRunner.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ cancelAnimation();
+ return;
+ }
+
// Adjust the wallpaper visibility for the showing home activity
final AppWindowToken recentsComponentAppToken =
dc.getHomeStack().getTopChild().getTopFullscreenAppToken();
@@ -296,6 +306,7 @@
// We've already canceled the animation
return;
}
+ mService.mH.removeCallbacks(mFailsafeRunnable);
mCanceled = true;
try {
mRunner.onAnimationCanceled();
@@ -321,10 +332,21 @@
}
mPendingAnimations.clear();
+ mRunner.asBinder().unlinkToDeath(this, 0);
+
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
}
+ void scheduleFailsafe() {
+ mService.mH.postDelayed(mFailsafeRunnable, FAILSAFE_DELAY);
+ }
+
+ @Override
+ public void binderDied() {
+ cancelAnimation();
+ }
+
void checkAnimationReady(WallpaperController wallpaperController) {
if (mPendingStart) {
final boolean wallpaperReady = !isHomeAppOverWallpaper()
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 379a1a1..3be7b235 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -26,6 +26,7 @@
import android.graphics.Rect;
import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Slog;
@@ -47,7 +48,7 @@
/**
* Helper class to run app animations in a remote process.
*/
-class RemoteAnimationController {
+class RemoteAnimationController implements DeathRecipient {
private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
private static final long TIMEOUT_MS = 2000;
@@ -56,12 +57,10 @@
private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
private final Rect mTmpRect = new Rect();
private final Handler mHandler;
- private FinishedCallback mFinishedCallback;
+ private final Runnable mTimeoutRunnable = this::cancelAnimation;
- private final Runnable mTimeoutRunnable = () -> {
- onAnimationFinished();
- invokeAnimationCancelled();
- };
+ private FinishedCallback mFinishedCallback;
+ private boolean mCanceled;
RemoteAnimationController(WindowManagerService service,
RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
@@ -90,7 +89,7 @@
* Called when the transition is ready to be started, and all leashes have been set up.
*/
void goodToGo() {
- if (mPendingAnimations.isEmpty()) {
+ if (mPendingAnimations.isEmpty() || mCanceled) {
onAnimationFinished();
return;
}
@@ -107,8 +106,8 @@
}
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
- mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
- mFinishedCallback);
+ mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0);
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, mFinishedCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start remote animation", e);
onAnimationFinished();
@@ -120,6 +119,17 @@
}
}
+ private void cancelAnimation() {
+ synchronized (mService.getWindowManagerLock()) {
+ if (mCanceled) {
+ return;
+ }
+ mCanceled = true;
+ }
+ onAnimationFinished();
+ invokeAnimationCancelled();
+ }
+
private void writeStartDebugStatement() {
Slog.i(TAG, "Starting remote animation");
final StringWriter sw = new StringWriter();
@@ -154,6 +164,7 @@
private void onAnimationFinished() {
mHandler.removeCallbacks(mTimeoutRunnable);
+ mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0);
synchronized (mService.mWindowMap) {
releaseFinishedCallback();
mService.openSurfaceTransaction();
@@ -193,6 +204,11 @@
mService.sendSetRunningRemoteAnimation(pid, running);
}
+ @Override
+ public void binderDied() {
+ cancelAnimation();
+ }
+
private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
RemoteAnimationController mOuter;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 6bceda5..6686b80 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -419,4 +419,10 @@
* @see android.view.IWindowManager#lockNow
*/
public abstract void lockNow();
+
+ /**
+ * Return the user that owns the given window, {@link android.os.UserHandle#USER_NULL} if
+ * the window token is not found.
+ */
+ public abstract int getWindowOwnerUserId(IBinder windowToken);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0b5c006..f1cd46b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -25,12 +25,10 @@
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
-import static android.content.Intent.EXTRA_USER_HANDLE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.os.UserHandle.USER_NULL;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DOCKED_INVALID;
@@ -181,7 +179,6 @@
import android.util.MergedConfiguration;
import android.util.Pair;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
@@ -2423,8 +2420,8 @@
final int oldRotation = defaultDisplayContent.getRotation();
final boolean oldAltOrientation = defaultDisplayContent.getAltOrientation();
- final int rotation = mPolicy.rotationForOrientationLw(lastOrientation,
- oldRotation);
+ final int rotation = mPolicy.rotationForOrientationLw(lastOrientation, oldRotation,
+ true /* defaultDisplay */);
boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
lastOrientation, rotation);
if (oldRotation == rotation && oldAltOrientation == altOrientation) {
@@ -2793,6 +2790,11 @@
mTaskSnapshotController.screenTurningOff(listener);
}
+ @Override
+ public void triggerAnimationFailsafe() {
+ mH.sendEmptyMessage(H.ANIMATION_FAILSAFE);
+ }
+
/**
* Starts deferring layout passes. Useful when doing multiple changes but to optimize
* performance, only one layout pass should be done. This can be called multiple times, and
@@ -4566,6 +4568,7 @@
public static final int NOTIFY_KEYGUARD_TRUSTED_CHANGED = 57;
public static final int SET_HAS_OVERLAY_UI = 58;
public static final int SET_RUNNING_REMOTE_ANIMATION = 59;
+ public static final int ANIMATION_FAILSAFE = 60;
/**
* Used to denote that an integer field in a message will not be used.
@@ -4984,6 +4987,14 @@
mAmInternal.setRunningRemoteAnimation(msg.arg1, msg.arg2 == 1);
}
break;
+ case ANIMATION_FAILSAFE: {
+ synchronized (mWindowMap) {
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.scheduleFailsafe();
+ }
+ }
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG_WM, "handleMessage: exit");
@@ -7351,6 +7362,17 @@
public void lockNow() {
WindowManagerService.this.lockNow(null);
}
+
+ @Override
+ public int getWindowOwnerUserId(IBinder token) {
+ synchronized (mWindowMap) {
+ WindowState window = mWindowMap.get(token);
+ if (window != null) {
+ return UserHandle.getUserId(window.mOwnerUid);
+ }
+ return UserHandle.USER_NULL;
+ }
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index 847222a..61b48c2 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -380,6 +380,7 @@
auto jProduct = make_javastr(env, prop10.product);
auto jVersion = make_javastr(env, prop10.version);
auto jSerial = make_javastr(env, prop10.serial);
+ constexpr bool isInitializationRequired = true;
bool isBgScanSupported = prop11 ? prop11->supportsBackgroundScanning : false;
auto jVendorInfo = prop11 ? VendorInfoFromHal(env, prop11->vendorInfo) : nullptr;
@@ -394,9 +395,9 @@
return make_javaref(env, env->NewObject(gjni.ModuleProperties.clazz,
gjni.ModuleProperties.cstor, moduleId, jServiceName.get(), prop10.classId,
jImplementor.get(), jProduct.get(), jVersion.get(), jSerial.get(), prop10.numTuners,
- prop10.numAudioSources, prop10.supportsCapture, jBands.get(), isBgScanSupported,
- jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(), nullptr,
- jVendorInfo.get()));
+ prop10.numAudioSources, isInitializationRequired, prop10.supportsCapture, jBands.get(),
+ isBgScanSupported, jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(),
+ nullptr, jVendorInfo.get()));
}
JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &properties,
@@ -712,7 +713,7 @@
gjni.ModuleProperties.clazz = MakeGlobalRefOrDie(env, modulePropertiesClass);
gjni.ModuleProperties.cstor = GetMethodIDOrDie(env, modulePropertiesClass, "<init>",
"(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;"
- "Ljava/lang/String;IIZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z"
+ "Ljava/lang/String;IIZZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z"
"[I[ILjava/util/Map;Ljava/util/Map;)V");
auto programInfoClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$ProgramInfo");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2fd7764..02cd3b6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -51,6 +51,7 @@
import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
import static android.app.admin.DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
@@ -590,7 +591,8 @@
List<String> mLockTaskPackages = new ArrayList<>();
// Bitfield of feature flags to be enabled during LockTask mode.
- int mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
+ // We default on the power button menu, in order to be consistent with pre-P behaviour.
+ int mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
boolean mStatusBarDisabled = false;
@@ -5531,10 +5533,11 @@
.setAttestationChallenge(null)
.build();
- final boolean generationResult = keyChain.generateKeyPair(algorithm,
+ final int generationResult = keyChain.generateKeyPair(algorithm,
new ParcelableKeyGenParameterSpec(noAttestationSpec));
- if (!generationResult) {
- Log.e(LOG_TAG, "KeyChain failed to generate a keypair.");
+ if (generationResult != KeyChain.KEY_GEN_SUCCESS) {
+ Log.e(LOG_TAG, String.format(
+ "KeyChain failed to generate a keypair, error %d.", generationResult));
return false;
}
@@ -5547,12 +5550,17 @@
final byte[] attestationChallenge = keySpec.getAttestationChallenge();
if (attestationChallenge != null) {
- final boolean attestationResult = keyChain.attestKey(
+ final int attestationResult = keyChain.attestKey(
alias, attestationChallenge, attestationUtilsFlags, attestationChain);
- if (!attestationResult) {
+ if (attestationResult != KeyChain.KEY_ATTESTATION_SUCCESS) {
Log.e(LOG_TAG, String.format(
- "Attestation for %s failed, deleting key.", alias));
+ "Attestation for %s failed (rc=%d), deleting key.",
+ alias, attestationResult));
keyChain.removeKeyPair(alias);
+ if (attestationResult == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) {
+ throw new UnsupportedOperationException(
+ "Device does not support Device ID attestation.");
+ }
return false;
}
}
@@ -8738,6 +8746,7 @@
@Override
public List getPermittedInputMethodsForCurrentUser() {
+ enforceManageUsers();
UserInfo currentUser;
try {
currentUser = mInjector.getIActivityManager().getCurrentUser();
@@ -9909,6 +9918,9 @@
boolean hasOverview = (flags & LOCK_TASK_FEATURE_OVERVIEW) != 0;
Preconditions.checkArgument(hasHome || !hasOverview,
"Cannot use LOCK_TASK_FEATURE_OVERVIEW without LOCK_TASK_FEATURE_HOME");
+ boolean hasNotification = (flags & LOCK_TASK_FEATURE_NOTIFICATIONS) != 0;
+ Preconditions.checkArgument(hasHome || !hasNotification,
+ "Cannot use LOCK_TASK_FEATURE_NOTIFICATIONS without LOCK_TASK_FEATURE_HOME");
final int userHandle = mInjector.userHandleGetCallingUserId();
synchronized (this) {
diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
index f603a09..fa41220 100644
--- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
@@ -147,6 +147,15 @@
Looper backupLooper = startBackupThreadAndGetLooper();
mShadowBackupLooper = shadowOf(backupLooper);
+
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ BackupAgentTimeoutParameters agentTimeoutParameters =
+ new BackupAgentTimeoutParameters(mainHandler, application.getContentResolver());
+ agentTimeoutParameters.start();
+
+ // We need to mock BMS timeout parameters before initializing the BackupHandler since
+ // the constructor of BackupHandler relies on the timeout parameters.
+ when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
mBackupHandler = new BackupHandler(mBackupManagerService, backupLooper);
mBackupManager = spy(FakeIBackupManager.class);
@@ -157,7 +166,8 @@
mTransportManager,
packageManager,
mBackupHandler,
- mWakeLock);
+ mWakeLock,
+ agentTimeoutParameters);
when(mBackupManagerService.getBaseStateDir()).thenReturn(mBaseStateDir);
when(mBackupManagerService.getDataDir()).thenReturn(dataDir);
when(mBackupManagerService.getBackupManagerBinder()).thenReturn(mBackupManager);
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 03792b1..92d6bbd 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -41,12 +41,14 @@
import android.app.backup.RestoreSet;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
import com.android.server.backup.internal.BackupHandler;
@@ -115,6 +117,15 @@
Looper backupLooper = startBackupThreadAndGetLooper();
mShadowBackupLooper = shadowOf(backupLooper);
+
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ BackupAgentTimeoutParameters agentTimeoutParameters =
+ new BackupAgentTimeoutParameters(mainHandler, application.getContentResolver());
+ agentTimeoutParameters.start();
+
+ // We need to mock BMS timeout parameters before initializing the BackupHandler since
+ // the constructor of BackupHandler relies on it.
+ when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
BackupHandler backupHandler = new BackupHandler(mBackupManagerService, backupLooper);
mWakeLock = createBackupWakeLock(application);
@@ -125,7 +136,8 @@
mTransportManager,
application.getPackageManager(),
backupHandler,
- mWakeLock);
+ mWakeLock,
+ agentTimeoutParameters);
when(mBackupManagerService.getPendingRestores()).thenReturn(new ArrayDeque<>());
}
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index c210fde..5a886e3 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -28,6 +28,7 @@
import android.os.PowerManager;
import android.util.SparseArray;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
import com.android.server.backup.internal.BackupHandler;
@@ -43,7 +44,8 @@
TransportManager transportManager,
PackageManager packageManager,
BackupHandler backupHandler,
- PowerManager.WakeLock wakeLock) {
+ PowerManager.WakeLock wakeLock,
+ BackupAgentTimeoutParameters agentTimeoutParameters) {
when(backupManagerService.getContext()).thenReturn(context);
when(backupManagerService.getTransportManager()).thenReturn(transportManager);
when(backupManagerService.getPackageManager()).thenReturn(packageManager);
@@ -53,6 +55,7 @@
when(backupManagerService.getCurrentOperations()).thenReturn(new SparseArray<>());
when(backupManagerService.getActivityManager()).thenReturn(mock(IActivityManager.class));
when(backupManagerService.getWakelock()).thenReturn(wakeLock);
+ when(backupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
}
public static PowerManager.WakeLock createBackupWakeLock(Application application) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 290de2b7..fe47de6 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3719,7 +3719,8 @@
}
private void verifyLockTaskState(int userId) throws Exception {
- verifyLockTaskState(userId, new String[0], DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
+ verifyLockTaskState(userId, new String[0],
+ DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS);
}
private void verifyLockTaskState(int userId, String[] packages, int flags) throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 9ae45ea..81a73efd 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -38,6 +38,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.os.FileUtils;
import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
@@ -49,7 +50,6 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
@@ -72,6 +72,9 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KeySyncTaskTest {
+
+ private static final String SNAPSHOT_TOP_LEVEL_DIRECTORY = "recoverablekeystore";
+
private static final String KEY_ALGORITHM = "AES";
private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private static final String TEST_ROOT_CERT_ALIAS = "trusted_root";
@@ -117,7 +120,7 @@
TEST_ROOT_CERT_ALIAS);
mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
TEST_ROOT_CERT_ALIAS);
- mRecoverySnapshotStorage = new RecoverySnapshotStorage();
+ mRecoverySnapshotStorage = new RecoverySnapshotStorage(context.getFilesDir());
mKeySyncTask = new KeySyncTask(
mRecoverableKeyStoreDb,
@@ -139,6 +142,10 @@
public void tearDown() {
mRecoverableKeyStoreDb.close();
mDatabaseFile.delete();
+
+ File file = new File(InstrumentationRegistry.getTargetContext().getFilesDir(),
+ SNAPSHOT_TOP_LEVEL_DIRECTORY);
+ FileUtils.deleteContentsAndDir(file);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index f5f5027..18a3885 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -189,7 +189,6 @@
mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
mMockContext,
- KeyStore.getInstance(),
mRecoverableKeyStoreDb,
mRecoverySessionStorage,
Executors.newSingleThreadExecutor(),
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
new file mode 100644
index 0000000..2f4da86
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2018 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.locksettings.recoverablekeystore.serialization;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.locksettings.recoverablekeystore.TestData;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.security.cert.CertPath;
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KeyChainSnapshotSerializerTest {
+ private static final int COUNTER_ID = 2134;
+ private static final int SNAPSHOT_VERSION = 125;
+ private static final int MAX_ATTEMPTS = 21;
+ private static final byte[] SERVER_PARAMS = new byte[] { 8, 2, 4 };
+ private static final byte[] KEY_BLOB = new byte[] { 124, 53, 53, 53 };
+ private static final byte[] PUBLIC_KEY_BLOB = new byte[] { 6, 6, 6, 6, 6, 6, 7 };
+ private static final CertPath CERT_PATH = TestData.CERT_PATH_1;
+ private static final int SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
+ private static final int LOCK_SCREEN_UI = KeyChainProtectionParams.UI_FORMAT_PASSWORD;
+ private static final byte[] SALT = new byte[] { 5, 4, 3, 2, 1 };
+ private static final int MEMORY_DIFFICULTY = 45;
+ private static final int ALGORITHM = KeyDerivationParams.ALGORITHM_SCRYPT;
+ private static final byte[] SECRET = new byte[] { 1, 2, 3, 4 };
+
+ private static final String TEST_KEY_1_ALIAS = "key1";
+ private static final byte[] TEST_KEY_1_BYTES = new byte[] { 66, 77, 88 };
+
+ private static final String TEST_KEY_2_ALIAS = "key2";
+ private static final byte[] TEST_KEY_2_BYTES = new byte[] { 99, 33, 11 };
+
+ private static final String TEST_KEY_3_ALIAS = "key3";
+ private static final byte[] TEST_KEY_3_BYTES = new byte[] { 2, 8, 100 };
+
+ @Test
+ public void roundTrip_persistsCounterId() throws Exception {
+ assertThat(roundTrip().getCounterId()).isEqualTo(COUNTER_ID);
+ }
+
+ @Test
+ public void roundTrip_persistsSnapshotVersion() throws Exception {
+ assertThat(roundTrip().getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION);
+ }
+
+ @Test
+ public void roundTrip_persistsMaxAttempts() throws Exception {
+ assertThat(roundTrip().getMaxAttempts()).isEqualTo(MAX_ATTEMPTS);
+ }
+
+ @Test
+ public void roundTrip_persistsRecoveryKey() throws Exception {
+ assertThat(roundTrip().getEncryptedRecoveryKeyBlob()).isEqualTo(KEY_BLOB);
+ }
+
+ @Test
+ public void roundTrip_persistsServerParams() throws Exception {
+ assertThat(roundTrip().getServerParams()).isEqualTo(SERVER_PARAMS);
+ }
+
+ @Test
+ public void roundTrip_persistsCertPath() throws Exception {
+ assertThat(roundTrip().getTrustedHardwareCertPath()).isEqualTo(CERT_PATH);
+ }
+
+ @Test
+ public void roundTrip_persistsBackendPublicKey() throws Exception {
+ assertThat(roundTrip().getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY_BLOB);
+ }
+
+ @Test
+ public void roundTrip_persistsParamsList() throws Exception {
+ assertThat(roundTrip().getKeyChainProtectionParams()).hasSize(1);
+ }
+
+ @Test
+ public void roundTripParams_persistsUserSecretType() throws Exception {
+ assertThat(roundTripParams().getUserSecretType()).isEqualTo(SECRET_TYPE);
+ }
+
+ @Test
+ public void roundTripParams_persistsLockScreenUi() throws Exception {
+ assertThat(roundTripParams().getLockScreenUiFormat()).isEqualTo(LOCK_SCREEN_UI);
+ }
+
+ @Test
+ public void roundTripParams_persistsSalt() throws Exception {
+ assertThat(roundTripParams().getKeyDerivationParams().getSalt()).isEqualTo(SALT);
+ }
+
+ @Test
+ public void roundTripParams_persistsAlgorithm() throws Exception {
+ assertThat(roundTripParams().getKeyDerivationParams().getAlgorithm()).isEqualTo(ALGORITHM);
+ }
+
+ @Test
+ public void roundTripParams_persistsMemoryDifficulty() throws Exception {
+ assertThat(roundTripParams().getKeyDerivationParams().getMemoryDifficulty())
+ .isEqualTo(MEMORY_DIFFICULTY);
+ }
+
+ @Test
+ public void roundTripParams_doesNotPersistSecret() throws Exception {
+ assertThat(roundTripParams().getSecret()).isEmpty();
+ }
+
+ @Test
+ public void roundTripKeys_hasCorrectLength() throws Exception {
+ assertThat(roundTripKeys()).hasSize(3);
+ }
+
+ @Test
+ public void roundTripKeys_0_persistsAlias() throws Exception {
+ assertThat(roundTripKeys().get(0).getAlias()).isEqualTo(TEST_KEY_1_ALIAS);
+ }
+
+ @Test
+ public void roundTripKeys_0_persistsKeyBytes() throws Exception {
+ assertThat(roundTripKeys().get(0).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_1_BYTES);
+ }
+
+ @Test
+ public void roundTripKeys_1_persistsAlias() throws Exception {
+ assertThat(roundTripKeys().get(1).getAlias()).isEqualTo(TEST_KEY_2_ALIAS);
+ }
+
+ @Test
+ public void roundTripKeys_1_persistsKeyBytes() throws Exception {
+ assertThat(roundTripKeys().get(1).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_2_BYTES);
+ }
+
+ @Test
+ public void roundTripKeys_2_persistsAlias() throws Exception {
+ assertThat(roundTripKeys().get(2).getAlias()).isEqualTo(TEST_KEY_3_ALIAS);
+ }
+
+ @Test
+ public void roundTripKeys_2_persistsKeyBytes() throws Exception {
+ assertThat(roundTripKeys().get(2).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_3_BYTES);
+ }
+
+ @Test
+ public void serialize_doesNotThrowForNullPublicKey() throws Exception {
+ KeyChainSnapshotSerializer.serialize(
+ createTestKeyChainSnapshotNoPublicKey(), new ByteArrayOutputStream());
+ }
+
+ private static List<WrappedApplicationKey> roundTripKeys() throws Exception {
+ return roundTrip().getWrappedApplicationKeys();
+ }
+
+ private static KeyChainProtectionParams roundTripParams() throws Exception {
+ return roundTrip().getKeyChainProtectionParams().get(0);
+ }
+
+ public static KeyChainSnapshot roundTrip() throws Exception {
+ KeyChainSnapshot snapshot = createTestKeyChainSnapshot();
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ KeyChainSnapshotSerializer.serialize(snapshot, byteArrayOutputStream);
+ return KeyChainSnapshotDeserializer.deserialize(
+ new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
+ }
+
+ private static KeyChainSnapshot createTestKeyChainSnapshot() throws Exception {
+ return new KeyChainSnapshot.Builder()
+ .setCounterId(COUNTER_ID)
+ .setSnapshotVersion(SNAPSHOT_VERSION)
+ .setServerParams(SERVER_PARAMS)
+ .setMaxAttempts(MAX_ATTEMPTS)
+ .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+ .setKeyChainProtectionParams(createKeyChainProtectionParamsList())
+ .setWrappedApplicationKeys(createKeys())
+ .setTrustedHardwareCertPath(CERT_PATH)
+ .setTrustedHardwarePublicKey(PUBLIC_KEY_BLOB)
+ .build();
+ }
+
+ private static KeyChainSnapshot createTestKeyChainSnapshotNoPublicKey() throws Exception {
+ return new KeyChainSnapshot.Builder()
+ .setCounterId(COUNTER_ID)
+ .setSnapshotVersion(SNAPSHOT_VERSION)
+ .setServerParams(SERVER_PARAMS)
+ .setMaxAttempts(MAX_ATTEMPTS)
+ .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+ .setKeyChainProtectionParams(createKeyChainProtectionParamsList())
+ .setWrappedApplicationKeys(createKeys())
+ .setTrustedHardwareCertPath(CERT_PATH)
+ .build();
+ }
+
+ private static List<WrappedApplicationKey> createKeys() {
+ ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
+ keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES));
+ keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES));
+ keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES));
+ return keyList;
+ }
+
+ private static List<KeyChainProtectionParams> createKeyChainProtectionParamsList() {
+ KeyDerivationParams keyDerivationParams =
+ KeyDerivationParams.createScryptParams(SALT, MEMORY_DIFFICULTY);
+ KeyChainProtectionParams keyChainProtectionParams = new KeyChainProtectionParams.Builder()
+ .setKeyDerivationParams(keyDerivationParams)
+ .setUserSecretType(SECRET_TYPE)
+ .setLockScreenUiFormat(LOCK_SCREEN_UI)
+ .setSecret(SECRET)
+ .build();
+ ArrayList<KeyChainProtectionParams> keyChainProtectionParamsList =
+ new ArrayList<>(1);
+ keyChainProtectionParamsList.add(keyChainProtectionParams);
+ return keyChainProtectionParamsList;
+ }
+
+ private static WrappedApplicationKey createKey(String alias, byte[] bytes) {
+ return new WrappedApplicationKey.Builder()
+ .setAlias(alias)
+ .setEncryptedKeyMaterial(bytes)
+ .build();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
index c772956..ad14c3a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
@@ -1,27 +1,82 @@
package com.android.server.locksettings.recoverablekeystore.storage;
+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.os.FileUtils;
+import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.server.locksettings.recoverablekeystore.TestData;
+import com.google.common.io.Files;
+
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertPath;
import java.security.cert.CertificateException;
import java.util.ArrayList;
+import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class RecoverySnapshotStorageTest {
- private static final KeyChainSnapshot MINIMAL_KEYCHAIN_SNAPSHOT =
- createMinimalKeyChainSnapshot();
+ private static final int COUNTER_ID = 432546;
+ private static final int MAX_ATTEMPTS = 10;
+ private static final byte[] SERVER_PARAMS = new byte[] { 12, 8, 2, 4, 15, 64 };
+ private static final byte[] KEY_BLOB = new byte[] { 124, 56, 53, 99, 0, 0, 1 };
+ private static final CertPath CERT_PATH = TestData.CERT_PATH_2;
+ private static final int SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
+ private static final int LOCK_SCREEN_UI = KeyChainProtectionParams.UI_FORMAT_PATTERN;
+ private static final byte[] SALT = new byte[] { 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1 };
+ private static final int MEMORY_DIFFICULTY = 12;
+ private static final byte[] SECRET = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0 };
- private final RecoverySnapshotStorage mRecoverySnapshotStorage = new RecoverySnapshotStorage();
+ private static final String TEST_KEY_1_ALIAS = "alias1";
+ private static final byte[] TEST_KEY_1_BYTES = new byte[] { 100, 32, 43, 66, 77, 88 };
+
+ private static final String TEST_KEY_2_ALIAS = "alias11";
+ private static final byte[] TEST_KEY_2_BYTES = new byte[] { 100, 0, 0, 99, 33, 11 };
+
+ private static final String TEST_KEY_3_ALIAS = "alias111";
+ private static final byte[] TEST_KEY_3_BYTES = new byte[] { 1, 1, 1, 0, 2, 8, 100 };
+
+ private static final int TEST_UID = 1000;
+ private static final String SNAPSHOT_DIRECTORY = "recoverablekeystore/snapshots";
+ private static final String SNAPSHOT_FILE_PATH = "1000.xml";
+ private static final String SNAPSHOT_TOP_LEVEL_DIRECTORY = "recoverablekeystore";
+
+ private static final KeyChainSnapshot MINIMAL_KEYCHAIN_SNAPSHOT =
+ createTestKeyChainSnapshot(1);
+
+ private Context mContext;
+ private RecoverySnapshotStorage mRecoverySnapshotStorage;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mRecoverySnapshotStorage = new RecoverySnapshotStorage(mContext.getFilesDir());
+ }
+
+ @After
+ public void tearDown() {
+ File file = new File(mContext.getFilesDir(), SNAPSHOT_TOP_LEVEL_DIRECTORY);
+ FileUtils.deleteContentsAndDir(file);
+ }
@Test
public void get_isNullForNonExistentSnapshot() {
@@ -30,37 +85,153 @@
@Test
public void get_returnsSetSnapshot() {
- int userId = 1000;
+ mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
- mRecoverySnapshotStorage.put(userId, MINIMAL_KEYCHAIN_SNAPSHOT);
-
- assertEquals(MINIMAL_KEYCHAIN_SNAPSHOT, mRecoverySnapshotStorage.get(userId));
+ assertEquals(MINIMAL_KEYCHAIN_SNAPSHOT, mRecoverySnapshotStorage.get(TEST_UID));
}
@Test
- public void remove_removesSnapshots() {
- int userId = 1000;
+ public void get_readsFromDiskIfNoneInMemory() {
+ mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+ RecoverySnapshotStorage storage = new RecoverySnapshotStorage(mContext.getFilesDir());
- mRecoverySnapshotStorage.put(userId, MINIMAL_KEYCHAIN_SNAPSHOT);
- mRecoverySnapshotStorage.remove(userId);
-
- assertNull(mRecoverySnapshotStorage.get(1000));
+ assertKeyChainSnapshotsAreEqual(MINIMAL_KEYCHAIN_SNAPSHOT, storage.get(TEST_UID));
}
- private static KeyChainSnapshot createMinimalKeyChainSnapshot() {
+ @Test
+ public void get_deletesFileIfItIsInvalidSnapshot() throws Exception {
+ File folder = new File(mContext.getFilesDir(), SNAPSHOT_DIRECTORY);
+ folder.mkdirs();
+ File file = new File(folder, SNAPSHOT_FILE_PATH);
+ byte[] fileContents = "<keyChainSnapshot></keyChainSnapshot>".getBytes(
+ StandardCharsets.UTF_8);
+ Files.write(fileContents, file);
+ assertTrue(file.exists());
+
+ assertNull(mRecoverySnapshotStorage.get(TEST_UID));
+
+ assertFalse(file.exists());
+ }
+
+ @Test
+ public void put_overwritesOldFiles() {
+ int snapshotVersion = 2;
+ mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+
+ mRecoverySnapshotStorage.put(TEST_UID, createTestKeyChainSnapshot(snapshotVersion));
+
+ KeyChainSnapshot snapshot = new RecoverySnapshotStorage(mContext.getFilesDir())
+ .get(TEST_UID);
+ assertEquals(snapshotVersion, snapshot.getSnapshotVersion());
+ }
+
+ @Test
+ public void put_doesNotThrowIfCannotCreateFiles() throws Exception {
+ File evilFile = new File(mContext.getFilesDir(), "recoverablekeystore");
+ Files.write(new byte[] { 1 }, evilFile);
+
+ mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+
+ assertNull(new RecoverySnapshotStorage(mContext.getFilesDir()).get(TEST_UID));
+ }
+
+ @Test
+ public void remove_removesSnapshotsFromMemory() {
+ mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+ mRecoverySnapshotStorage.remove(TEST_UID);
+
+ assertNull(mRecoverySnapshotStorage.get(TEST_UID));
+ }
+
+ @Test
+ public void remove_removesSnapshotsFromDisk() {
+ mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+
+ new RecoverySnapshotStorage(mContext.getFilesDir()).remove(TEST_UID);
+
+ assertNull(new RecoverySnapshotStorage(mContext.getFilesDir()).get(TEST_UID));
+ }
+
+ private void assertKeyChainSnapshotsAreEqual(KeyChainSnapshot a, KeyChainSnapshot b) {
+ assertEquals(b.getCounterId(), a.getCounterId());
+ assertEquals(b.getSnapshotVersion(), a.getSnapshotVersion());
+ assertArrayEquals(b.getServerParams(), a.getServerParams());
+ assertEquals(b.getMaxAttempts(), a.getMaxAttempts());
+ assertArrayEquals(b.getEncryptedRecoveryKeyBlob(), a.getEncryptedRecoveryKeyBlob());
+ assertEquals(b.getTrustedHardwareCertPath(), a.getTrustedHardwareCertPath());
+
+ List<WrappedApplicationKey> aKeys = a.getWrappedApplicationKeys();
+ List<WrappedApplicationKey> bKeys = b.getWrappedApplicationKeys();
+ assertEquals(bKeys.size(), aKeys.size());
+ for (int i = 0; i < aKeys.size(); i++) {
+ assertWrappedApplicationKeysAreEqual(aKeys.get(i), bKeys.get(i));
+ }
+
+ List<KeyChainProtectionParams> aParams = a.getKeyChainProtectionParams();
+ List<KeyChainProtectionParams> bParams = b.getKeyChainProtectionParams();
+ assertEquals(bParams.size(), aParams.size());
+ for (int i = 0; i < aParams.size(); i++) {
+ assertKeyChainProtectionParamsAreEqual(aParams.get(i), bParams.get(i));
+ }
+ }
+
+ private void assertWrappedApplicationKeysAreEqual(
+ WrappedApplicationKey a, WrappedApplicationKey b) {
+ assertEquals(b.getAlias(), a.getAlias());
+ assertArrayEquals(b.getEncryptedKeyMaterial(), a.getEncryptedKeyMaterial());
+ }
+
+ private void assertKeyChainProtectionParamsAreEqual(
+ KeyChainProtectionParams a, KeyChainProtectionParams b) {
+ assertEquals(b.getUserSecretType(), a.getUserSecretType());
+ assertEquals(b.getLockScreenUiFormat(), a.getLockScreenUiFormat());
+ assertKeyDerivationParamsAreEqual(a.getKeyDerivationParams(), b.getKeyDerivationParams());
+ }
+
+ private void assertKeyDerivationParamsAreEqual(KeyDerivationParams a, KeyDerivationParams b) {
+ assertEquals(b.getAlgorithm(), a.getAlgorithm());
+ assertEquals(b.getMemoryDifficulty(), a.getMemoryDifficulty());
+ assertArrayEquals(b.getSalt(), a.getSalt());
+ }
+
+ private static KeyChainSnapshot createTestKeyChainSnapshot(int snapshotVersion) {
+ KeyDerivationParams keyDerivationParams =
+ KeyDerivationParams.createScryptParams(SALT, MEMORY_DIFFICULTY);
+ KeyChainProtectionParams keyChainProtectionParams = new KeyChainProtectionParams.Builder()
+ .setKeyDerivationParams(keyDerivationParams)
+ .setUserSecretType(SECRET_TYPE)
+ .setLockScreenUiFormat(LOCK_SCREEN_UI)
+ .setSecret(SECRET)
+ .build();
+ ArrayList<KeyChainProtectionParams> keyChainProtectionParamsList =
+ new ArrayList<>(1);
+ keyChainProtectionParamsList.add(keyChainProtectionParams);
+
+ ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
+ keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES));
+ keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES));
+ keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES));
+
try {
return new KeyChainSnapshot.Builder()
- .setCounterId(1)
- .setSnapshotVersion(1)
- .setServerParams(new byte[0])
- .setMaxAttempts(10)
- .setEncryptedRecoveryKeyBlob(new byte[0])
- .setKeyChainProtectionParams(new ArrayList<>())
- .setWrappedApplicationKeys(new ArrayList<>())
- .setTrustedHardwareCertPath(TestData.CERT_PATH_1)
+ .setCounterId(COUNTER_ID)
+ .setSnapshotVersion(snapshotVersion)
+ .setServerParams(SERVER_PARAMS)
+ .setMaxAttempts(MAX_ATTEMPTS)
+ .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+ .setKeyChainProtectionParams(keyChainProtectionParamsList)
+ .setWrappedApplicationKeys(keyList)
+ .setTrustedHardwareCertPath(CERT_PATH)
.build();
} catch (CertificateException e) {
throw new RuntimeException(e);
}
}
+
+ private static WrappedApplicationKey createKey(String alias, byte[] bytes) {
+ return new WrappedApplicationKey.Builder()
+ .setAlias(alias)
+ .setEncryptedKeyMaterial(bytes)
+ .build();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 601999d..c2a0ccf 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -281,6 +281,7 @@
MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext());
mInjector = new MyInjector(myContext, Looper.getMainLooper());
mController = setupController();
+ setChargingState(mController, false);
}
@Test
@@ -381,8 +382,6 @@
@Test
public void testForcedIdle() throws Exception {
- setChargingState(mController, false);
-
mController.forceIdleState(PACKAGE_1, USER_ID, true);
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
@@ -395,8 +394,6 @@
@Test
public void testNotificationEvent() throws Exception {
- setChargingState(mController, false);
-
reportEvent(mController, USER_INTERACTION, 0);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
mInjector.mElapsedRealtime = 1;
@@ -410,8 +407,6 @@
@Test
public void testSlicePinnedEvent() throws Exception {
- setChargingState(mController, false);
-
reportEvent(mController, USER_INTERACTION, 0);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
mInjector.mElapsedRealtime = 1;
@@ -425,8 +420,6 @@
@Test
public void testSlicePinnedPrivEvent() throws Exception {
- setChargingState(mController, false);
-
mController.forceIdleState(PACKAGE_1, USER_ID, true);
reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
@@ -434,14 +427,13 @@
@Test
public void testPredictionTimedout() throws Exception {
- setChargingState(mController, false);
// Set it to timeout or usage, so that prediction can override it
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
- REASON_MAIN_TIMEOUT, 1 * HOUR_MS);
+ REASON_MAIN_TIMEOUT, HOUR_MS);
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_MAIN_PREDICTED, 1 * HOUR_MS);
+ REASON_MAIN_PREDICTED, HOUR_MS);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
// Fast forward 12 hours
@@ -464,7 +456,6 @@
@Test
public void testOverrides() throws Exception {
- setChargingState(mController, false);
// Can force to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_MAIN_FORCED, 1 * HOUR_MS);
@@ -494,8 +485,6 @@
@Test
public void testTimeout() throws Exception {
- setChargingState(mController, false);
-
reportEvent(mController, USER_INTERACTION, 0);
assertBucket(STANDBY_BUCKET_ACTIVE);
@@ -505,19 +494,19 @@
assertBucket(STANDBY_BUCKET_ACTIVE);
// bucketing works after timeout
- mInjector.mElapsedRealtime = FREQUENT_THRESHOLD - 100;
+ mInjector.mElapsedRealtime = mController.mPredictionTimeoutMillis - 100;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_WORKING_SET);
-
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
+ // Use recent prediction
assertBucket(STANDBY_BUCKET_FREQUENT);
+
+ // Way past prediction timeout, use system thresholds
+ mInjector.mElapsedRealtime = RARE_THRESHOLD * 4;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_RARE);
}
@Test
public void testCascadingTimeouts() throws Exception {
- setChargingState(mController, false);
-
reportEvent(mController, USER_INTERACTION, 0);
assertBucket(STANDBY_BUCKET_ACTIVE);
@@ -539,8 +528,6 @@
@Test
public void testOverlappingTimeouts() throws Exception {
- setChargingState(mController, false);
-
reportEvent(mController, USER_INTERACTION, 0);
assertBucket(STANDBY_BUCKET_ACTIVE);
@@ -596,8 +583,6 @@
@Test
public void testPredictionNotOverridden() throws Exception {
- setChargingState(mController, false);
-
reportEvent(mController, USER_INTERACTION, 0);
assertBucket(STANDBY_BUCKET_ACTIVE);
@@ -623,6 +608,31 @@
}
@Test
+ public void testPredictionStrikesBack() throws Exception {
+ reportEvent(mController, USER_INTERACTION, 0);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+
+ // Predict to FREQUENT
+ mInjector.mElapsedRealtime = RARE_THRESHOLD;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
+ assertBucket(STANDBY_BUCKET_FREQUENT);
+
+ // Add a short timeout event
+ mInjector.mElapsedRealtime += 1000;
+ reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+ mInjector.mElapsedRealtime += 1000;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+
+ // Verify it reverted to predicted
+ mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD / 2;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_FREQUENT);
+ }
+
+ @Test
public void testAddActiveDeviceAdmin() {
assertActiveAdmins(USER_ID, (String[]) null);
assertActiveAdmins(USER_ID2, (String[]) null);
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 553d658..95361f0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -17,14 +17,20 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Binder;
+import android.os.IInterface;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -50,7 +56,7 @@
* atest FrameworksServicesTests:com.android.server.wm.RemoteAnimationControllerTest
*/
@SmallTest
-@FlakyTest(detail = "Promote to presubmit if non-flakyness is established")
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class RemoteAnimationControllerTest extends WindowTestsBase {
@@ -67,6 +73,7 @@
public void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
+ when(mMockRunner.asBinder()).thenReturn(new Binder());
mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
mAdapter.setCallingPid(123);
sWm.mH.runWithScissors(() -> {
@@ -166,7 +173,7 @@
@Test
public void testZeroAnimations() throws Exception {
mController.goodToGo();
- verifyZeroInteractions(mMockRunner);
+ verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@Test
@@ -175,7 +182,7 @@
mController.createAnimationAdapter(win.mAppToken,
new Point(50, 100), new Rect(50, 100, 150, 150));
mController.goodToGo();
- verifyZeroInteractions(mMockRunner);
+ verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@Test
@@ -206,7 +213,12 @@
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
win.mAppToken.removeImmediately();
mController.goodToGo();
- verifyZeroInteractions(mMockRunner);
+ verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
verify(mFinishedCallback).onAnimationFinished(eq(adapter));
}
+
+ private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
+ verify(binder, atLeast(0)).asBinder();
+ verifyNoMoreInteractions(binder);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 6d9167f..1af344b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -445,8 +445,7 @@
}
@Override
- public int rotationForOrientationLw(int orientation,
- int lastRotation) {
+ public int rotationForOrientationLw(int orientation, int lastRotation, boolean defaultDisplay) {
return rotationToReport;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 09d88fd..0815876 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -367,8 +367,8 @@
mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
- mHelper.createNotificationChannel(PKG, UID, channel2, false);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
mHelper.setShowBadge(PKG, UID, true);
@@ -427,10 +427,10 @@
mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
- mHelper.createNotificationChannel(PKG, UID, channel2, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, false);
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+ mHelper.createNotificationChannel(PKG, UID, channel3, false, false);
+ mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
mHelper.setShowBadge(PKG, UID, true);
@@ -481,7 +481,7 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
@@ -507,7 +507,7 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
loadStreamXml(baos, true);
@@ -528,7 +528,7 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
loadStreamXml(baos, true);
@@ -569,7 +569,7 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(null, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
loadStreamXml(baos, true);
@@ -593,9 +593,9 @@
mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
- mHelper.createNotificationChannel(PKG, UID, channel2, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+ mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
mHelper.deleteNotificationChannel(PKG, UID, channel1.getId());
mHelper.deleteNotificationChannelGroup(PKG, UID, ncg.getId());
@@ -701,7 +701,7 @@
@Test
public void testDeletesDefaultChannelAfterChannelIsCreated() throws Exception {
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
@@ -721,7 +721,7 @@
ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
loadStreamXml(baos, false);
@@ -734,36 +734,39 @@
mHelper.setImportance(PKG, UID, IMPORTANCE_NONE);
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
}
@Test
public void testCreateChannel_badImportance() throws Exception {
try {
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1),
+ true, false);
fail("Was allowed to create a channel with invalid importance");
} catch (IllegalArgumentException e) {
// yay
}
try {
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED),
+ true, false);
fail("Was allowed to create a channel with invalid importance");
} catch (IllegalArgumentException e) {
// yay
}
try {
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1),
+ true, false);
fail("Was allowed to create a channel with invalid importance");
} catch (IllegalArgumentException e) {
// yay
}
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true, false);
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true, false);
}
@@ -777,7 +780,7 @@
channel.setBypassDnd(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.createNotificationChannel(PKG, UID, channel, false);
+ mHelper.createNotificationChannel(PKG, UID, channel, false, false);
// same id, try to update all fields
final NotificationChannel channel2 =
@@ -824,7 +827,7 @@
public void testUpdate_postUpgrade_noUpdateAppFields() throws Exception {
final NotificationChannel channel = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel, false);
+ mHelper.createNotificationChannel(PKG, UID, channel, false, false);
assertTrue(mHelper.canShowBadge(PKG, UID));
assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
@@ -865,7 +868,7 @@
}
channel.lockFields(lockMask);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
NotificationChannel savedChannel =
mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
@@ -894,7 +897,7 @@
}
channel.lockFields(lockMask);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
NotificationChannel savedChannel =
mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
@@ -920,7 +923,7 @@
@Test
public void testLockFields_soundAndVibration() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
update1.setSound(new Uri.Builder().scheme("test").build(),
@@ -944,7 +947,7 @@
@Test
public void testLockFields_vibrationAndLights() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
update1.setVibrationPattern(new long[]{7945, 46 ,246});
@@ -964,7 +967,7 @@
@Test
public void testLockFields_lightsAndImportance() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
update1.setLightColor(Color.GREEN);
@@ -984,7 +987,7 @@
@Test
public void testLockFields_visibilityAndDndAndBadge() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
assertEquals(0,
mHelper.getNotificationChannel(PKG, UID, getChannel().getId(), false)
.getUserLockedFields());
@@ -1029,7 +1032,7 @@
channel.enableVibration(true);
channel.setVibrationPattern(new long[]{100, 67, 145, 156});
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
// Does not return deleted channel
@@ -1058,8 +1061,8 @@
NotificationChannel channel2 =
new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
channelMap.put(channel2.getId(), channel2);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
- mHelper.createNotificationChannel(PKG, UID, channel2, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
@@ -1091,9 +1094,9 @@
new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
NotificationChannel channel3 =
new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
- mHelper.createNotificationChannel(PKG, UID, channel2, true);
- mHelper.createNotificationChannel(PKG, UID, channel3, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
@@ -1109,14 +1112,14 @@
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel.setVibrationPattern(vibration);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
NotificationChannel newChannel = new NotificationChannel(
channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
newChannel.setVibrationPattern(new long[]{100});
- mHelper.createNotificationChannel(PKG, UID, newChannel, true);
+ mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
// No long deleted, using old settings
compareChannels(channel,
@@ -1128,7 +1131,7 @@
assertTrue(mHelper.onlyHasDefaultChannel(PKG, UID));
assertFalse(mHelper.onlyHasDefaultChannel(UPDATED_PKG, UID2));
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
assertFalse(mHelper.onlyHasDefaultChannel(PKG, UID));
}
@@ -1136,7 +1139,7 @@
public void testCreateChannel_defaultChannelId() throws Exception {
try {
mHelper.createNotificationChannel(PKG, UID, new NotificationChannel(
- NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true);
+ NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true, false);
fail("Allowed to create default channel");
} catch (IllegalArgumentException e) {
// pass
@@ -1150,13 +1153,13 @@
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel.setVibrationPattern(vibration);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
NotificationChannel newChannel = new NotificationChannel(
channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
newChannel.setVibrationPattern(new long[]{100});
- mHelper.createNotificationChannel(PKG, UID, newChannel, true);
+ mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
// Old settings not overridden
compareChannels(channel,
@@ -1169,7 +1172,7 @@
final NotificationChannel channel = new NotificationChannel("id2", "name2",
NotificationManager.IMPORTANCE_DEFAULT);
channel.setSound(sound, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
assertEquals(sound, mHelper.getNotificationChannel(
PKG, UID, channel.getId(), false).getSound());
}
@@ -1181,8 +1184,8 @@
NotificationChannel channel2 =
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
- mHelper.createNotificationChannel(PKG, UID, channel2, false);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
mHelper.permanentlyDeleteNotificationChannels(PKG, UID);
@@ -1205,9 +1208,9 @@
mHelper.createNotificationChannelGroup(PKG, UID, notDeleted, true);
mHelper.createNotificationChannelGroup(PKG, UID, deleted, true);
- mHelper.createNotificationChannel(PKG, UID, nonGroupedNonDeletedChannel, true);
- mHelper.createNotificationChannel(PKG, UID, groupedAndDeleted, true);
- mHelper.createNotificationChannel(PKG, UID, groupedButNotDeleted, true);
+ mHelper.createNotificationChannel(PKG, UID, nonGroupedNonDeletedChannel, true, false);
+ mHelper.createNotificationChannel(PKG, UID, groupedAndDeleted, true, false);
+ mHelper.createNotificationChannel(PKG, UID, groupedButNotDeleted, true, false);
mHelper.deleteNotificationChannelGroup(PKG, UID, deleted.getId());
@@ -1264,14 +1267,14 @@
// Deleted
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
// Not deleted
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
assertEquals(2, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
@@ -1302,7 +1305,7 @@
@Test
public void testOnPackageChange_downgradeTargetSdk() throws Exception {
// create channel as api 26
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true);
+ mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
// install new app version targeting 25
final ApplicationInfo legacy = new ApplicationInfo();
@@ -1338,7 +1341,7 @@
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup("garbage");
try {
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
fail("Created a channel with a bad group");
} catch (IllegalArgumentException e) {
}
@@ -1351,7 +1354,7 @@
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
assertEquals(ncg.getId(),
mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false).getGroup());
@@ -1369,20 +1372,20 @@
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
NotificationChannel channel1a =
new NotificationChannel("id1a", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1a.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1a, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1a, true, false);
NotificationChannel channel2 =
new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
channel2.setGroup(ncg2.getId());
- mHelper.createNotificationChannel(PKG, UID, channel2, true);
+ mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
NotificationChannel channel3 =
new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel3, true);
+ mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
List<NotificationChannelGroup> actual =
mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
@@ -1416,7 +1419,7 @@
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
channel1.setImportance(IMPORTANCE_LOW);
@@ -1436,12 +1439,12 @@
@Test
public void testCreateChannel_updateName() throws Exception {
NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, nc, true);
+ mHelper.createNotificationChannel(PKG, UID, nc, true, false);
NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
assertEquals("hello", actual.getName());
nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, nc, true);
+ mHelper.createNotificationChannel(PKG, UID, nc, true, false);
actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
assertEquals("goodbye", actual.getName());
@@ -1455,13 +1458,13 @@
NotificationChannelGroup group = new NotificationChannelGroup("group", "");
mHelper.createNotificationChannelGroup(PKG, UID, group, true);
NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, nc, true);
+ mHelper.createNotificationChannel(PKG, UID, nc, true, false);
NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
assertNull(actual.getGroup());
nc = new NotificationChannel("id", "hello", IMPORTANCE_HIGH);
nc.setGroup(group.getId());
- mHelper.createNotificationChannel(PKG, UID, nc, true);
+ mHelper.createNotificationChannel(PKG, UID, nc, true, false);
actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
assertNotNull(actual.getGroup());
@@ -1486,7 +1489,7 @@
int numChannels = ThreadLocalRandom.current().nextInt(1, 10);
for (int j = 0; j < numChannels; j++) {
mHelper.createNotificationChannel(pkgName, UID,
- new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true);
+ new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false);
}
expectedChannels.put(pkgName, numChannels);
}
@@ -1621,10 +1624,10 @@
c.setGroup(group.getId());
NotificationChannel d = new NotificationChannel("d", "d", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, a, true);
- mHelper.createNotificationChannel(PKG, UID, b, true);
- mHelper.createNotificationChannel(PKG, UID, c, true);
- mHelper.createNotificationChannel(PKG, UID, d, true);
+ mHelper.createNotificationChannel(PKG, UID, a, true, false);
+ mHelper.createNotificationChannel(PKG, UID, b, true, false);
+ mHelper.createNotificationChannel(PKG, UID, c, true, false);
+ mHelper.createNotificationChannel(PKG, UID, d, true, false);
mHelper.deleteNotificationChannel(PKG, UID, c.getId());
NotificationChannelGroup retrieved = mHelper.getNotificationChannelGroupWithChannels(
@@ -1641,22 +1644,31 @@
@Test
public void testAndroidPkgCanBypassDnd_creation() {
-
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
test.setBypassDnd(true);
- mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true);
+ mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
.canBypassDnd());
}
@Test
+ public void testDndPkgCanBypassDnd_creation() {
+ NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ test.setBypassDnd(true);
+
+ mHelper.createNotificationChannel(PKG, UID, test, true, true);
+
+ assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+ }
+
+ @Test
public void testNormalPkgCannotBypassDnd_creation() {
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
test.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, 1000, test, true);
+ mHelper.createNotificationChannel(PKG, 1000, test, true, false);
assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
}
@@ -1664,11 +1676,11 @@
@Test
public void testAndroidPkgCanBypassDnd_update() throws Exception {
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true);
+ mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
update.setBypassDnd(true);
- mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true);
+ mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true, false);
assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
.canBypassDnd());
@@ -1678,12 +1690,24 @@
}
@Test
- public void testNormalPkgCannotBypassDnd_update() {
+ public void testDndPkgCanBypassDnd_update() throws Exception {
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, 1000, test, true);
+ mHelper.createNotificationChannel(PKG, UID, test, true, true);
+
NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
update.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, 1000, update, true);
+ mHelper.createNotificationChannel(PKG, UID, update, true, true);
+
+ assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+ }
+
+ @Test
+ public void testNormalPkgCannotBypassDnd_update() {
+ NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(PKG, 1000, test, true, false);
+ NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ update.setBypassDnd(true);
+ mHelper.createNotificationChannel(PKG, 1000, update, true, false);
assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
}
}
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index fd28b65..271f813 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -70,6 +70,8 @@
private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>();
private static final long ONE_MINUTE = 60 * 1000;
+ private static final int STANDBY_BUCKET_UNKNOWN = -1;
+
@VisibleForTesting
static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
private static final String TAG_PACKAGES = "packages";
@@ -111,6 +113,9 @@
long lastUsedScreenTime;
// Last predicted time using elapsed timebase
long lastPredictedTime;
+ // Last predicted bucket
+ @UsageStatsManager.StandbyBuckets
+ int lastPredictedBucket = STANDBY_BUCKET_UNKNOWN;
// Standby bucket
@UsageStatsManager.StandbyBuckets
int currentBucket;
@@ -342,6 +347,7 @@
appUsageHistory.bucketingReason = reason;
if ((reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED) {
appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime);
+ appUsageHistory.lastPredictedBucket = bucket;
}
if (DEBUG) {
Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
@@ -350,6 +356,17 @@
}
/**
+ * Update the prediction for the app but don't change the actual bucket
+ * @param app The app for which the prediction was made
+ * @param elapsedTimeAdjusted The elapsed time in the elapsed duration timebase
+ * @param bucket The predicted bucket
+ */
+ public void updateLastPrediction(AppUsageHistory app, long elapsedTimeAdjusted, int bucket) {
+ app.lastPredictedTime = elapsedTimeAdjusted;
+ app.lastPredictedBucket = bucket;
+ }
+
+ /**
* Marks the last time a job was run, with the given elapsedRealtime. The time stored is
* based on the elapsed timebase.
* @param packageName
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 9139a4c..571ed00a 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -22,6 +22,7 @@
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
@@ -30,13 +31,14 @@
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED_PRIV;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
-
import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
@@ -538,19 +540,30 @@
}
final int oldBucket = app.currentBucket;
int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
- boolean predictionLate = false;
+ boolean predictionLate = predictionTimedOut(app, elapsedRealtime);
// Compute age-based bucket
if (oldMainReason == REASON_MAIN_DEFAULT
|| oldMainReason == REASON_MAIN_USAGE
|| oldMainReason == REASON_MAIN_TIMEOUT
- || (predictionLate = predictionTimedOut(app, elapsedRealtime))) {
- newBucket = getBucketForLocked(packageName, userId,
- elapsedRealtime);
- if (DEBUG) {
- Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
+ || predictionLate) {
+
+ if (!predictionLate && app.lastPredictedBucket >= STANDBY_BUCKET_ACTIVE
+ && app.lastPredictedBucket <= STANDBY_BUCKET_RARE) {
+ newBucket = app.lastPredictedBucket;
+ reason = REASON_MAIN_PREDICTED | REASON_SUB_PREDICTED_RESTORED;
+ if (DEBUG) {
+ Slog.d(TAG, "Restored predicted newBucket = " + newBucket);
+ }
+ } else {
+ newBucket = getBucketForLocked(packageName, userId,
+ elapsedRealtime);
+ if (DEBUG) {
+ Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
+ }
+ reason = REASON_MAIN_TIMEOUT;
}
- reason = REASON_MAIN_TIMEOUT;
}
+
// Check if the app is within one of the timeouts for forced bucket elevation
final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
if (newBucket >= STANDBY_BUCKET_ACTIVE
@@ -587,8 +600,7 @@
/** Returns true if there hasn't been a prediction for the app in a while. */
private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
- return (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED
- && app.lastPredictedTime > 0
+ return app.lastPredictedTime > 0
&& mAppIdleHistory.getElapsedTime(elapsedRealtime)
- app.lastPredictedTime > mPredictionTimeoutMillis;
}
@@ -747,6 +759,8 @@
case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
+ case UsageEvents.Event.SLICE_PINNED: return REASON_SUB_USAGE_SLICE_PINNED;
+ case UsageEvents.Event.SLICE_PINNED_PRIV: return REASON_SUB_USAGE_SLICE_PINNED_PRIV;
default: return 0;
}
}
@@ -1032,6 +1046,10 @@
if (predicted) {
// Check if the app is within one of the timeouts for forced bucket elevation
final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
+ // In case of not using the prediction, just keep track of it for applying after
+ // ACTIVE or WORKING_SET timeout.
+ mAppIdleHistory.updateLastPrediction(app, elapsedTimeAdjusted, newBucket);
+
if (newBucket > STANDBY_BUCKET_ACTIVE
&& app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_ACTIVE;
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index 9dc07c1..da04a0d 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -920,11 +920,11 @@
try {
if (!token.createNewFile()) {
throw new RuntimeException("Failed to create download token for request "
- + request);
+ + request + ". Token location is " + token.getPath());
}
} catch (IOException e) {
throw new RuntimeException("Failed to create download token for request " + request
- + " due to IOException " + e);
+ + " due to IOException " + e + ". Attempted to write to " + token.getPath());
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index da5bd84..15e0632 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7752,11 +7752,25 @@
*/
public static final int INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED = 0x4;
+ /**
+ * The indication for link capacity estimate update.
+ * @hide
+ */
+ public static final int INDICATION_FILTER_LINK_CAPACITY_ESTIMATE = 0x8;
+
+ /**
+ * The indication for physical channel config update.
+ * @hide
+ */
+ public static final int INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG = 0x10;
+
/** @hide */
@IntDef(flag = true, prefix = { "INDICATION_FILTER_" }, value = {
INDICATION_FILTER_SIGNAL_STRENGTH,
INDICATION_FILTER_FULL_NETWORK_STATE,
- INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED
+ INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED,
+ INDICATION_FILTER_LINK_CAPACITY_ESTIMATE,
+ INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG
})
@Retention(RetentionPolicy.SOURCE)
public @interface IndicationFilters{}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index b0c00c6..fe7533f 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -297,7 +297,9 @@
for (Uri tempFileUri : tempFiles) {
if (verifyTempFilePath(context, request.getFileServiceId(), tempFileUri)) {
File tempFile = new File(tempFileUri.getSchemeSpecificPart());
- tempFile.delete();
+ if (!tempFile.delete()) {
+ Log.w(LOG_TAG, "Failed to delete temp file at " + tempFile.getPath());
+ }
}
}
}
@@ -474,6 +476,8 @@
if (!MbmsUtils.isContainedIn(
MbmsUtils.getEmbmsTempFileDirForService(context, serviceId), tempFile)) {
+ Log.w(LOG_TAG, "File at " + path + " is not contained in the temp file root," +
+ " which is " + MbmsUtils.getEmbmsTempFileDirForService(context, serviceId));
return false;
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index ee7084a..d25fd3f 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -419,6 +419,8 @@
int RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING = 145;
int RIL_REQUEST_START_KEEPALIVE = 146;
int RIL_REQUEST_STOP_KEEPALIVE = 147;
+ int RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA = 148;
+ int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 149;
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 300c701..499f254 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -211,7 +211,7 @@
signature != atoms.signatures.end(); signature++) {
int argIndex;
- fprintf(out, "void\n");
+ fprintf(out, "int\n");
fprintf(out, "stats_write(int32_t code");
argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -251,7 +251,7 @@
" diff length: %s vs %s\");\n",
attributionDecl.fields.front().name.c_str(),
chainField.name.c_str());
- fprintf(out, " return;\n");
+ fprintf(out, " return -EINVAL;\n");
fprintf(out, " }\n");
}
}
@@ -284,7 +284,7 @@
argIndex++;
}
- fprintf(out, " event.write(LOG_ID_STATS);\n");
+ fprintf(out, " return event.write(LOG_ID_STATS);\n");
fprintf(out, "}\n");
fprintf(out, "\n");
}
@@ -293,7 +293,7 @@
signature != atoms.non_chained_signatures.end(); signature++) {
int argIndex;
- fprintf(out, "void\n");
+ fprintf(out, "int\n");
fprintf(out, "stats_write_non_chained(int32_t code");
argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -327,7 +327,7 @@
argIndex++;
}
- fprintf(out, " event.write(LOG_ID_STATS);\n");
+ fprintf(out, " return event.write(LOG_ID_STATS);\n");
fprintf(out, "}\n");
fprintf(out, "\n");
}
@@ -377,7 +377,7 @@
const AtomDecl &attributionDecl) {
for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
signature != signatures.end(); signature++) {
- fprintf(out, "void %s(int32_t code ", method_name.c_str());
+ fprintf(out, "int %s(int32_t code ", method_name.c_str());
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
@@ -522,7 +522,7 @@
const AtomDecl &attributionDecl) {
for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
signature != signatures.end(); signature++) {
- fprintf(out, " public static native void %s(int code", method_name.c_str());
+ fprintf(out, " public static native int %s(int code", method_name.c_str());
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
@@ -719,7 +719,7 @@
result += java_type_signature(*arg);
}
}
- result += ")V";
+ result += ")I";
return result;
}
@@ -732,7 +732,7 @@
signature != signatures.end(); signature++) {
int argIndex;
- fprintf(out, "static void\n");
+ fprintf(out, "static int\n");
fprintf(out, "%s(JNIEnv* env, jobject clazz UNUSED, jint code",
jni_function_name(java_method_name, *signature).c_str());
argIndex = 1;
@@ -779,7 +779,7 @@
"\"java/lang/IllegalArgumentException\", "
"\"invalid attribution field(%s) length.\");\n",
chainField.name.c_str());
- fprintf(out, " return;\n");
+ fprintf(out, " return -EINVAL;\n");
fprintf(out, " }\n");
}
if (chainField.javaType == JAVA_TYPE_INT) {
@@ -822,7 +822,7 @@
// stats_write call
argIndex = 1;
- fprintf(out, " android::util::%s(code", cpp_method_name.c_str());
+ fprintf(out, " int ret = android::util::%s(code", cpp_method_name.c_str());
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
@@ -868,6 +868,7 @@
}
argIndex++;
}
+ fprintf(out, " return ret;\n");
fprintf(out, "}\n");
fprintf(out, "\n");
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index c8df087..433285b 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1317,6 +1317,7 @@
if (pin) {
NetworkRequest request = new NetworkRequest.Builder()
.clearCapabilities()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build();
NetworkPinner.pin(mContext, request);