Merge changes from topic "sfinput3"
* changes:
WindowManager: Communicate with input system by WindowTokens.
Plumbing for SurfaceControl#setInputWindowInfo.
diff --git a/api/current.txt b/api/current.txt
index ea6f190..99ea60f 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -29000,11 +29000,14 @@
method public boolean is5GHzBandSupported();
method public boolean isDeviceToApRttSupported();
method public boolean isEnhancedPowerReportingSupported();
+ method public boolean isOweSupported();
method public boolean isP2pSupported();
method public boolean isPreferredNetworkOffloadSupported();
method public deprecated boolean isScanAlwaysAvailable();
method public boolean isTdlsSupported();
method public boolean isWifiEnabled();
+ method public boolean isWpa3SaeSupported();
+ method public boolean isWpa3SuiteBSupported();
method public deprecated boolean pingSupplicant();
method public deprecated boolean reassociate();
method public deprecated boolean reconnect();
@@ -43326,6 +43329,7 @@
public class PhoneStateListener {
ctor public PhoneStateListener();
+ ctor public PhoneStateListener(java.util.concurrent.Executor);
method public void onCallForwardingIndicatorChanged(boolean);
method public void onCallStateChanged(int, java.lang.String);
method public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
diff --git a/api/system-current.txt b/api/system-current.txt
index fa49f07..8eb5507 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -137,6 +137,7 @@
field public static final java.lang.String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
field public static final java.lang.String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
field public static final java.lang.String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
+ field public static final java.lang.String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final java.lang.String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
field public static final java.lang.String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
field public static final java.lang.String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
@@ -4545,10 +4546,6 @@
public final class Settings {
field public static final java.lang.String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
field public static final java.lang.String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
- field public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10; // 0xa
- field public static final int USER_SETUP_PERSONALIZATION_NOT_STARTED = 0; // 0x0
- field public static final int USER_SETUP_PERSONALIZATION_PAUSED = 2; // 0x2
- field public static final int USER_SETUP_PERSONALIZATION_STARTED = 1; // 0x1
}
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
@@ -4592,6 +4589,10 @@
field public static final java.lang.String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
field public static final java.lang.String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count";
field public static final java.lang.String USER_SETUP_COMPLETE = "user_setup_complete";
+ field public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10; // 0xa
+ field public static final int USER_SETUP_PERSONALIZATION_NOT_STARTED = 0; // 0x0
+ field public static final int USER_SETUP_PERSONALIZATION_PAUSED = 2; // 0x2
+ field public static final int USER_SETUP_PERSONALIZATION_STARTED = 1; // 0x1
field public static final java.lang.String USER_SETUP_PERSONALIZATION_STATE = "user_setup_personalization_state";
field public static final java.lang.String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
}
@@ -5461,6 +5462,7 @@
method public static android.os.PersistableBundle getDefaultConfig();
method public void overrideConfig(int, android.os.PersistableBundle);
method public void updateConfigForPhoneId(int, java.lang.String);
+ field public static final java.lang.String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
field public static final java.lang.String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
}
@@ -5535,8 +5537,10 @@
public class PhoneStateListener {
method public void onRadioPowerStateChanged(int);
method public void onSrvccStateChanged(int);
+ method public void onVoiceActivationStateChanged(int);
field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
field public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
+ field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
}
public class ServiceState implements android.os.Parcelable {
@@ -6600,6 +6604,22 @@
method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
}
+ public class ProvisioningManager {
+ method public static android.telephony.ims.ProvisioningManager createForSubscriptionId(android.content.Context, int);
+ method public int getProvisioningIntValue(int);
+ method public java.lang.String getProvisioningStringValue(int);
+ method public void registerProvisioningChangedCallback(java.util.concurrent.Executor, android.telephony.ims.ProvisioningManager.Callback);
+ method public int setProvisioningIntValue(int, int);
+ method public int setProvisioningStringValue(int, java.lang.String);
+ method public void unregisterProvisioningChangedCallback(android.telephony.ims.ProvisioningManager.Callback);
+ }
+
+ public static class ProvisioningManager.Callback {
+ ctor public ProvisioningManager.Callback();
+ method public void onProvisioningIntChanged(int, int);
+ method public void onProvisioningStringChanged(int, java.lang.String);
+ }
+
}
package android.telephony.ims.feature {
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index eb498f5..a981997 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -370,11 +370,9 @@
// This skips the uid map if it's an empty config.
if (it->second->getNumMetrics() > 0) {
uint64_t uidMapToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP);
- if (it->second->hashStringInReport()) {
- mUidMap->appendUidMap(dumpTimeStampNs, key, &str_set, proto);
- } else {
- mUidMap->appendUidMap(dumpTimeStampNs, key, nullptr, proto);
- }
+ mUidMap->appendUidMap(
+ dumpTimeStampNs, key, it->second->hashStringInReport() ? &str_set : nullptr,
+ it->second->versionStringsInReport(), it->second->installerInReport(), proto);
proto->end(uidMapToken);
}
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 27685fc..7fa05be 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -787,21 +787,24 @@
}
Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
- const vector<String16>& app) {
+ const vector<String16>& version_string,
+ const vector<String16>& app,
+ const vector<String16>& installer) {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informAllUidData was called");
- mUidMap->updateMap(getElapsedRealtimeNs(), uid, version, app);
+ mUidMap->updateMap(getElapsedRealtimeNs(), uid, version, version_string, app, installer);
VLOG("StatsService::informAllUidData succeeded");
return Status::ok();
}
-Status StatsService::informOnePackage(const String16& app, int32_t uid, int64_t version) {
+Status StatsService::informOnePackage(const String16& app, int32_t uid, int64_t version,
+ const String16& version_string, const String16& installer) {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informOnePackage was called");
- mUidMap->updateApp(getElapsedRealtimeNs(), app, uid, version);
+ mUidMap->updateApp(getElapsedRealtimeNs(), app, uid, version, version_string, installer);
return Status::ok();
}
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 4a5f05f..cd4d601 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -73,8 +73,10 @@
virtual Status informAlarmForSubscriberTriggeringFired();
virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
- const vector<String16>& app);
- virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version);
+ const vector<String16>& version_string,
+ const vector<String16>& app, const vector<String16>& installer);
+ virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version,
+ const String16& version_string, const String16& installer);
virtual Status informOnePackageRemoved(const String16& app, int32_t uid);
virtual Status informDeviceShutdown();
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 53d9673..244974d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -192,6 +192,9 @@
NativeProcessMemoryState native_process_memory_state = 10036;
CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037;
OnDevicePowerMeasurement on_device_power_measurement = 10038;
+ DeviceCalculatedPowerUse device_calculated_power_use = 10039;
+ DeviceCalculatedPowerBlameUid device_calculated_power_blame_uid = 10040;
+ DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = 10041;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -3198,3 +3201,63 @@
// Time spent in frequency in milliseconds, since thread start.
optional uint32 time_millis = 7;
}
+
+/**
+ * Pulls on-device BatteryStats power use calculations for the overall device.
+ */
+message DeviceCalculatedPowerUse {
+ // Power used by the device in mAh, as computed by BatteryStats, since BatteryStats last reset
+ // (i.e. roughly since device was last significantly charged).
+ // Currently, this is BatteryStatsHelper.getComputedPower() (not getTotalPower()).
+ optional float computed_power_milli_amp_hours = 1;
+}
+
+/**
+ * Pulls on-device BatteryStats power use calculations broken down by uid.
+ * This atom should be complemented by DeviceCalculatedPowerBlameOther, which contains the power use
+ * that is attributed to non-uid items. They must all be included to get the total power use.
+ */
+message DeviceCalculatedPowerBlameUid {
+ // Uid being blamed. Note: isolated uids have already been mapped to host uid.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Power used by this uid in mAh, as computed by BatteryStats, since BatteryStats last reset
+ // (i.e. roughly since device was last significantly charged).
+ optional float power_milli_amp_hours = 2;
+}
+
+/**
+ * Pulls on-device BatteryStats power use calculations that are not due to a uid, broken down by
+ * drain type.
+ * This atom should be complemented by DeviceCalculatedPowerBlameUid, which contains the blame that
+ * is attributed uids. They must all be included to get the total power use.
+ */
+message DeviceCalculatedPowerBlameOther {
+ // The type of item whose power use is being reported.
+ enum DrainType {
+ AMBIENT_DISPLAY = 0;
+ // reserved 1; reserved "APP"; // Logged instead in DeviceCalculatedPowerBlameUid.
+ BLUETOOTH = 2;
+ CAMERA = 3;
+ // Cell-standby
+ CELL = 4;
+ FLASHLIGHT = 5;
+ IDLE = 6;
+ MEMORY = 7;
+ // Amount that total computed drain exceeded the drain estimated using the
+ // battery level changes and capacity.
+ OVERCOUNTED = 8;
+ PHONE = 9;
+ SCREEN = 10;
+ // Amount that total computed drain was below the drain estimated using the
+ // battery level changes and capacity.
+ UNACCOUNTED = 11;
+ // reserved 12; reserved "USER"; // Entire drain for a user. This is NOT supported.
+ WIFI = 13;
+ }
+ optional DrainType drain_type = 1;
+
+ // Power used by this item in mAh, as computed by BatteryStats, since BatteryStats last reset
+ // (i.e. roughly since device was last significantly charged).
+ optional float power_milli_amp_hours = 2;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 8378ae1..0e131cb 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -243,6 +243,20 @@
{2, 3, 4, 5, 6},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
+ // DeviceCalculatedPowerUse.
+ {android::util::DEVICE_CALCULATED_POWER_USE,
+ {{}, {}, 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
+ // DeviceCalculatedPowerBlameUid.
+ {android::util::DEVICE_CALCULATED_POWER_BLAME_UID,
+ {{}, {}, // BatteryStats already merged isolated with host ids so it's unnecessary here.
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
+ // DeviceCalculatedPowerBlameOther.
+ {android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER,
+ {{}, {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index febb922..625294c 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -156,9 +156,6 @@
FieldValue(Field(mTagId, getSimpleField(1)), Value(speakerImpedance.speakerLocation)));
mValues.push_back(
FieldValue(Field(mTagId, getSimpleField(2)), Value(speakerImpedance.milliOhms)));
- if (!mValues.empty()) {
- mValues.back().mField.decorateLastPos(1);
- }
}
LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
@@ -173,9 +170,6 @@
FieldValue(Field(mTagId, getSimpleField(2)), Value(hardwareFailed.hardwareLocation)));
mValues.push_back(
FieldValue(Field(mTagId, getSimpleField(3)), Value(int32_t(hardwareFailed.errorCode))));
- if (!mValues.empty()) {
- mValues.back().mField.decorateLastPos(1);
- }
}
LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
@@ -190,9 +184,6 @@
FieldValue(Field(mTagId, getSimpleField(2)), Value(physicalDropDetected.accelPeak)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)),
Value(physicalDropDetected.freefallDuration)));
- if (!mValues.empty()) {
- mValues.back().mField.decorateLastPos(1);
- }
}
LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
@@ -205,10 +196,6 @@
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 1)),
Value(chargeCycles.cycleBucket[i])));
}
-
- if (!mValues.empty()) {
- mValues.back().mField.decorateLastPos(1);
- }
}
LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
@@ -231,10 +218,6 @@
Value(batteryHealthSnapshotArgs.resistanceMicroOhm)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)),
Value(batteryHealthSnapshotArgs.levelPercent)));
-
- if (!mValues.empty()) {
- mValues.back().mField.decorateLastPos(1);
- }
}
LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const SlowIo& slowIo) {
@@ -247,10 +230,6 @@
FieldValue(Field(mTagId, getSimpleField(1)), Value(int32_t(slowIo.operation))));
pos[0]++;
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(slowIo.count)));
-
- if (!mValues.empty()) {
- mValues.back().mField.decorateLastPos(1);
- }
}
LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
@@ -261,10 +240,6 @@
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
Value(batteryCausedShutdown.voltageMicroV)));
-
- if (!mValues.empty()) {
- mValues.back().mField.decorateLastPos(1);
- }
}
LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, 0) {}
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 4244d5b..ac34f47 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -77,6 +77,8 @@
mActivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, mNoReportMetricIds);
mHashStringsInReport = config.hash_strings_in_metric_report();
+ mVersionStringsInReport = config.version_strings_in_metric_report();
+ mInstallerInReport = config.installer_in_metric_report();
if (config.allowed_log_source_size() == 0) {
mConfigValid = false;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index a4672b6..a31efbd 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -83,6 +83,14 @@
return mHashStringsInReport;
};
+ inline bool versionStringsInReport() const {
+ return mVersionStringsInReport;
+ };
+
+ inline bool installerInReport() const {
+ return mInstallerInReport;
+ };
+
void refreshTtl(const int64_t currentTimestampNs) {
if (mTtlNs > 0) {
mTtlEndNs = currentTimestampNs + mTtlNs;
@@ -126,6 +134,8 @@
bool mConfigValid = false;
bool mHashStringsInReport = false;
+ bool mVersionStringsInReport = false;
+ bool mInstallerInReport = false;
const int64_t mTtlNs;
int64_t mTtlEndNs;
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 37a0067..59f3f04 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -49,6 +49,10 @@
const int FIELD_ID_SNAPSHOT_PACKAGE_UID = 3;
const int FIELD_ID_SNAPSHOT_PACKAGE_DELETED = 4;
const int FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH = 5;
+const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING = 6;
+const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH = 7;
+const int FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER = 8;
+const int FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH = 9;
const int FIELD_ID_SNAPSHOT_TIMESTAMP = 1;
const int FIELD_ID_SNAPSHOT_PACKAGE_INFO = 2;
const int FIELD_ID_SNAPSHOTS = 1;
@@ -60,6 +64,10 @@
const int FIELD_ID_CHANGE_NEW_VERSION = 5;
const int FIELD_ID_CHANGE_PREV_VERSION = 6;
const int FIELD_ID_CHANGE_PACKAGE_HASH = 7;
+const int FIELD_ID_CHANGE_NEW_VERSION_STRING = 8;
+const int FIELD_ID_CHANGE_PREV_VERSION_STRING = 9;
+const int FIELD_ID_CHANGE_NEW_VERSION_STRING_HASH = 10;
+const int FIELD_ID_CHANGE_PREV_VERSION_STRING_HASH = 11;
UidMap::UidMap() : mBytesUsed(0) {}
@@ -104,7 +112,8 @@
}
void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
- const vector<int64_t>& versionCode, const vector<String16>& packageName) {
+ const vector<int64_t>& versionCode, const vector<String16>& versionString,
+ const vector<String16>& packageName, const vector<String16>& installer) {
vector<wp<PackageInfoListener>> broadcastList;
{
lock_guard<mutex> lock(mMutex); // Exclusively lock for updates.
@@ -121,7 +130,9 @@
mMap.clear();
for (size_t j = 0; j < uid.size(); j++) {
string package = string(String8(packageName[j]).string());
- mMap[std::make_pair(uid[j], package)] = AppData(versionCode[j]);
+ mMap[std::make_pair(uid[j], package)] =
+ AppData(versionCode[j], string(String8(versionString[j]).string()),
+ string(String8(installer[j]).string()));
}
for (const auto& kv : deletedApps) {
@@ -150,23 +161,30 @@
}
void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid,
- const int64_t& versionCode) {
+ const int64_t& versionCode, const String16& versionString,
+ const String16& installer) {
vector<wp<PackageInfoListener>> broadcastList;
string appName = string(String8(app_16).string());
{
lock_guard<mutex> lock(mMutex);
int32_t prevVersion = 0;
+ string prevVersionString = "";
+ string newVersionString = string(String8(versionString).string());
bool found = false;
auto it = mMap.find(std::make_pair(uid, appName));
if (it != mMap.end()) {
found = true;
prevVersion = it->second.versionCode;
+ prevVersionString = it->second.versionString;
it->second.versionCode = versionCode;
+ it->second.versionString = newVersionString;
+ it->second.installer = string(String8(installer).string());
it->second.deleted = false;
}
if (!found) {
// Otherwise, we need to add an app at this uid.
- mMap[std::make_pair(uid, appName)] = AppData(versionCode);
+ mMap[std::make_pair(uid, appName)] =
+ AppData(versionCode, newVersionString, string(String8(installer).string()));
} else {
// Only notify the listeners if this is an app upgrade. If this app is being installed
// for the first time, then we don't notify the listeners.
@@ -174,7 +192,8 @@
// app after deletion.
getListenerListCopyLocked(&broadcastList);
}
- mChanges.emplace_back(false, timestamp, appName, uid, versionCode, prevVersion);
+ mChanges.emplace_back(false, timestamp, appName, uid, versionCode, newVersionString,
+ prevVersion, prevVersionString);
mBytesUsed += kBytesChangeRecord;
ensureBytesUsedBelowLimit();
StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
@@ -226,10 +245,12 @@
lock_guard<mutex> lock(mMutex);
int64_t prevVersion = 0;
+ string prevVersionString = "";
auto key = std::make_pair(uid, app);
auto it = mMap.find(key);
if (it != mMap.end() && !it->second.deleted) {
prevVersion = it->second.versionCode;
+ prevVersionString = it->second.versionString;
it->second.deleted = true;
mDeletedApps.push_back(key);
}
@@ -240,7 +261,7 @@
mMap.erase(oldest);
StatsdStats::getInstance().noteUidMapAppDeletionDropped();
}
- mChanges.emplace_back(true, timestamp, app, uid, 0, prevVersion);
+ mChanges.emplace_back(true, timestamp, app, uid, 0, "", prevVersion, prevVersionString);
mBytesUsed += kBytesChangeRecord;
ensureBytesUsedBelowLimit();
StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
@@ -315,8 +336,9 @@
return mBytesUsed;
}
-void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key,
- std::set<string> *str_set, ProtoOutputStream* proto) {
+void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set,
+ bool includeVersionStrings, bool includeInstaller,
+ ProtoOutputStream* proto) {
lock_guard<mutex> lock(mMutex); // Lock for updates
for (const ChangeRecord& record : mChanges) {
@@ -330,8 +352,22 @@
str_set->insert(record.package);
proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_PACKAGE_HASH,
(long long)Hash64(record.package));
+ if (includeVersionStrings) {
+ str_set->insert(record.versionString);
+ proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_NEW_VERSION_STRING_HASH,
+ (long long)Hash64(record.versionString));
+ str_set->insert(record.prevVersionString);
+ proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_PREV_VERSION_STRING_HASH,
+ (long long)Hash64(record.prevVersionString));
+ }
} else {
proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package);
+ if (includeVersionStrings) {
+ proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_NEW_VERSION_STRING,
+ record.versionString);
+ proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PREV_VERSION_STRING,
+ record.prevVersionString);
+ }
}
proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid);
@@ -354,8 +390,26 @@
str_set->insert(kv.first.second);
proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH,
(long long)Hash64(kv.first.second));
+ if (includeVersionStrings) {
+ str_set->insert(kv.second.versionString);
+ proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH,
+ (long long)Hash64(kv.second.versionString));
+ }
+ if (includeInstaller) {
+ str_set->insert(kv.second.installer);
+ proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH,
+ (long long)Hash64(kv.second.installer));
+ }
} else {
proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
+ if (includeVersionStrings) {
+ proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING,
+ kv.second.versionString);
+ }
+ if (includeInstaller) {
+ proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER,
+ kv.second.installer);
+ }
}
proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
@@ -391,8 +445,9 @@
for (const auto& kv : mMap) {
if (!kv.second.deleted) {
- dprintf(out, "%s, v%" PRId64 " (%i)\n", kv.first.second.c_str(), kv.second.versionCode,
- kv.first.first);
+ dprintf(out, "%s, v%" PRId64 ", %s, %s (%i)\n", kv.first.second.c_str(),
+ kv.second.versionCode, kv.second.versionString.c_str(),
+ kv.second.installer.c_str(), kv.first.first);
}
}
}
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 4598369..75ff507 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -44,12 +44,16 @@
struct AppData {
int64_t versionCode;
+ string versionString;
+ string installer;
bool deleted;
// Empty constructor needed for unordered map.
AppData() {
}
- AppData(const int64_t v) : versionCode(v), deleted(false){};
+
+ AppData(const int64_t v, const string& versionString, const string& installer)
+ : versionCode(v), versionString(versionString), installer(installer), deleted(false){};
};
// When calling appendUidMap, we retrieve all the ChangeRecords since the last
@@ -61,15 +65,20 @@
const int32_t uid;
const int64_t version;
const int64_t prevVersion;
+ const string versionString;
+ const string prevVersionString;
ChangeRecord(const bool isDeletion, const int64_t timestampNs, const string& package,
- const int32_t uid, const int64_t version, const int64_t prevVersion)
+ const int32_t uid, const int64_t version, const string versionString,
+ const int64_t prevVersion, const string prevVersionString)
: deletion(isDeletion),
timestampNs(timestampNs),
package(package),
uid(uid),
version(version),
- prevVersion(prevVersion) {
+ prevVersion(prevVersion),
+ versionString(versionString),
+ prevVersionString(prevVersionString) {
}
};
@@ -87,10 +96,12 @@
* tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
*/
void updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
- const vector<int64_t>& versionCode, const vector<String16>& packageName);
+ const vector<int64_t>& versionCode, const vector<String16>& versionString,
+ const vector<String16>& packageName, const vector<String16>& installer);
void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid,
- const int64_t& versionCode);
+ const int64_t& versionCode, const String16& versionString,
+ const String16& installer);
void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid);
// Returns true if the given uid contains the specified app (eg. com.google.android.gms).
@@ -127,8 +138,9 @@
// Gets all snapshots and changes that have occurred since the last output.
// If every config key has received a change or snapshot record, then this
// record is deleted.
- void appendUidMap(const int64_t& timestamp, const ConfigKey& key,
- std::set<string> *str_set, util::ProtoOutputStream* proto);
+ void appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set,
+ bool includeVersionStrings, bool includeInstaller,
+ util::ProtoOutputStream* proto);
// Forces the output to be cleared. We still generate a snapshot based on the current state.
// This results in extra data uploaded but helps us reconstruct the uid mapping on the server
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 5d0f3d1..32ee5af 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -233,6 +233,14 @@
optional bool deleted = 4;
optional uint64 name_hash = 5;
+
+ optional string version_string = 6;
+
+ optional uint64 version_string_hash = 7;
+
+ optional string installer = 8;
+
+ optional uint64 installer_hash = 9;
}
optional int64 elapsed_timestamp_nanos = 1;
@@ -250,6 +258,10 @@
optional int64 new_version = 5;
optional int64 prev_version = 6;
optional uint64 app_hash = 7;
+ optional string new_version_string = 8;
+ optional string prev_version_string = 9;
+ optional uint64 new_version_string_hash = 10;
+ optional uint64 prev_version_string_hash = 11;
}
repeated Change changes = 2;
}
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index aa789c7..f955df2 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -409,6 +409,10 @@
repeated MetricActivation metric_activation = 17;
+ optional bool version_strings_in_metric_report = 18;
+
+ optional bool installer_in_metric_report = 19;
+
// Field number 1000 is reserved for later use.
reserved 1000;
}
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 4c6671d..2b9528f 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -148,8 +148,12 @@
uidMap.updateMap(
1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+ {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+ android::String16("v1"), android::String16("v2")},
{android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
- android::String16("Pkg2"), android::String16("PkG3")} /* package name list */);
+ android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+ {android::String16(""), android::String16(""), android::String16(""),
+ android::String16(""), android::String16("")});
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
@@ -297,8 +301,12 @@
UidMap uidMap;
uidMap.updateMap(
1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+ {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+ android::String16("v1"), android::String16("v2")},
{android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
- android::String16("Pkg2"), android::String16("PkG3")} /* package name list */);
+ android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+ {android::String16(""), android::String16(""), android::String16(""),
+ android::String16(""), android::String16("")});
AttributionNodeInternal attribution_node1;
attribution_node1.set_uid(1111);
@@ -372,8 +380,12 @@
UidMap uidMap;
uidMap.updateMap(
1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+ {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+ android::String16("v1"), android::String16("v2")},
{android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
- android::String16("Pkg2"), android::String16("PkG3")} /* package name list */);
+ android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+ {android::String16(""), android::String16(""), android::String16(""),
+ android::String16(""), android::String16("")});
AttributionNodeInternal attribution_node1;
attribution_node1.set_uid(1067);
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 8864252..355df29 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -153,7 +153,8 @@
// Setup simple config key corresponding to empty config.
sp<UidMap> m = new UidMap();
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- m->updateMap(1, {1, 2}, {1, 2}, {String16("p1"), String16("p2")});
+ m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")},
+ {String16("p1"), String16("p2")}, {String16(""), String16("")});
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
@@ -182,7 +183,8 @@
// Setup simple config key corresponding to empty config.
sp<UidMap> m = new UidMap();
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- m->updateMap(1, {1, 2}, {1, 2}, {String16("p1"), String16("p2")});
+ m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")},
+ {String16("p1"), String16("p2")}, {String16(""), String16("")});
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index 99082cc..f0d9cf1 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -71,14 +71,20 @@
vector<int32_t> uids;
vector<int64_t> versions;
vector<String16> apps;
+ vector<String16> versionStrings;
+ vector<String16> installers;
uids.push_back(1000);
uids.push_back(1000);
+ versionStrings.push_back(String16("v1"));
+ versionStrings.push_back(String16("v1"));
+ installers.push_back(String16(""));
+ installers.push_back(String16(""));
apps.push_back(String16(kApp1.c_str()));
apps.push_back(String16(kApp2.c_str()));
versions.push_back(4);
versions.push_back(5);
- m.updateMap(1, uids, versions, apps);
+ m.updateMap(1, uids, versions, versionStrings, apps, installers);
EXPECT_TRUE(m.hasApp(1000, kApp1));
EXPECT_TRUE(m.hasApp(1000, kApp2));
EXPECT_FALSE(m.hasApp(1000, "not.app"));
@@ -97,14 +103,20 @@
vector<int32_t> uids;
vector<int64_t> versions;
vector<String16> apps;
+ vector<String16> versionStrings;
+ vector<String16> installers;
uids.push_back(1000);
uids.push_back(1000);
+ versionStrings.push_back(String16("v1"));
+ versionStrings.push_back(String16("v1"));
+ installers.push_back(String16(""));
+ installers.push_back(String16(""));
apps.push_back(String16(kApp1.c_str()));
apps.push_back(String16(kApp2.c_str()));
versions.push_back(4);
versions.push_back(5);
- m.updateMap(1, uids, versions, apps);
+ m.updateMap(1, uids, versions, versionStrings, apps, installers);
std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
EXPECT_EQ(name_set.size(), 2u);
@@ -112,7 +124,7 @@
EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
// Update the app1 version.
- m.updateApp(2, String16(kApp1.c_str()), 1000, 40);
+ m.updateApp(2, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16(""));
EXPECT_EQ(40, m.getAppVersion(1000, kApp1));
name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
@@ -138,14 +150,15 @@
TEST(UidMapTest, TestUpdateApp) {
UidMap m;
- m.updateMap(1, {1000, 1000}, {4, 5}, {String16(kApp1.c_str()), String16(kApp2.c_str())});
+ m.updateMap(1, {1000, 1000}, {4, 5}, {String16("v4"), String16("v5")},
+ {String16(kApp1.c_str()), String16(kApp2.c_str())}, {String16(""), String16("")});
std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
EXPECT_EQ(name_set.size(), 2u);
EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
// Adds a new name for uid 1000.
- m.updateApp(2, String16("NeW_aPP1_NAmE"), 1000, 40);
+ m.updateApp(2, String16("NeW_aPP1_NAmE"), 1000, 40, String16("v40"), String16(""));
name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
EXPECT_EQ(name_set.size(), 3u);
EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
@@ -154,7 +167,7 @@
EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end());
// This name is also reused by another uid 2000.
- m.updateApp(3, String16("NeW_aPP1_NAmE"), 2000, 1);
+ m.updateApp(3, String16("NeW_aPP1_NAmE"), 2000, 1, String16("v1"), String16(""));
name_set = m.getAppNamesFromUid(2000, true /* returnNormalized */);
EXPECT_EQ(name_set.size(), 1u);
EXPECT_TRUE(name_set.find("NeW_aPP1_NAmE") == name_set.end());
@@ -185,21 +198,26 @@
vector<int32_t> uids;
vector<int64_t> versions;
vector<String16> apps;
+ vector<String16> versionStrings;
+ vector<String16> installers;
uids.push_back(1000);
apps.push_back(String16(kApp2.c_str()));
+ versionStrings.push_back(String16("v1"));
+ installers.push_back(String16(""));
versions.push_back(5);
- m.updateMap(1, uids, versions, apps);
+ m.updateMap(1, uids, versions, versionStrings, apps, installers);
// Set the last timestamp for this config key to be newer.
m.mLastUpdatePerConfigKey[config1] = 2;
ProtoOutputStream proto;
- m.appendUidMap(3, config1, nullptr, &proto);
+ m.appendUidMap(3, config1, nullptr, true, true, &proto);
// Check there's still a uidmap attached this one.
UidMapping results;
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(1, results.snapshots_size());
+ EXPECT_EQ("v1", results.snapshots(0).package_info(0).version_string());
}
TEST(UidMapTest, TestRemovedAppRetained) {
@@ -209,15 +227,19 @@
m.OnConfigUpdated(config1);
vector<int32_t> uids;
vector<int64_t> versions;
+ vector<String16> versionStrings;
+ vector<String16> installers;
vector<String16> apps;
uids.push_back(1000);
apps.push_back(String16(kApp2.c_str()));
versions.push_back(5);
- m.updateMap(1, uids, versions, apps);
+ versionStrings.push_back(String16("v5"));
+ installers.push_back(String16(""));
+ m.updateMap(1, uids, versions, versionStrings, apps, installers);
m.removeApp(2, String16(kApp2.c_str()), 1000);
ProtoOutputStream proto;
- m.appendUidMap(3, config1, nullptr, &proto);
+ m.appendUidMap(3, config1, nullptr, true, true, &proto);
// Snapshot should still contain this item as deleted.
UidMapping results;
@@ -233,30 +255,34 @@
m.OnConfigUpdated(config1);
vector<int32_t> uids;
vector<int64_t> versions;
+ vector<String16> versionStrings;
+ vector<String16> installers;
vector<String16> apps;
const int maxDeletedApps = StatsdStats::kMaxDeletedAppsInUidMap;
for (int j = 0; j < maxDeletedApps + 10; j++) {
uids.push_back(j);
apps.push_back(String16(kApp1.c_str()));
versions.push_back(j);
+ versionStrings.push_back(String16("v"));
+ installers.push_back(String16(""));
}
- m.updateMap(1, uids, versions, apps);
+ m.updateMap(1, uids, versions, versionStrings, apps, installers);
// First, verify that we have the expected number of items.
UidMapping results;
ProtoOutputStream proto;
- m.appendUidMap(3, config1, nullptr, &proto);
+ m.appendUidMap(3, config1, nullptr, true, true, &proto);
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(maxDeletedApps + 10, results.snapshots(0).package_info_size());
// Now remove all the apps.
- m.updateMap(1, uids, versions, apps);
+ m.updateMap(1, uids, versions, versionStrings, apps, installers);
for (int j = 0; j < maxDeletedApps + 10; j++) {
m.removeApp(4, String16(kApp1.c_str()), j);
}
proto.clear();
- m.appendUidMap(5, config1, nullptr, &proto);
+ m.appendUidMap(5, config1, nullptr, true, true, &proto);
// Snapshot drops the first nine items.
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(maxDeletedApps, results.snapshots(0).package_info_size());
@@ -272,6 +298,8 @@
vector<int32_t> uids;
vector<int64_t> versions;
+ vector<String16> versionStrings;
+ vector<String16> installers;
vector<String16> apps;
uids.push_back(1000);
uids.push_back(1000);
@@ -279,45 +307,49 @@
apps.push_back(String16(kApp2.c_str()));
versions.push_back(4);
versions.push_back(5);
- m.updateMap(1, uids, versions, apps);
+ versionStrings.push_back(String16("v4"));
+ versionStrings.push_back(String16("v5"));
+ installers.push_back(String16(""));
+ installers.push_back(String16(""));
+ m.updateMap(1, uids, versions, versionStrings, apps, installers);
ProtoOutputStream proto;
- m.appendUidMap(2, config1, nullptr, &proto);
+ m.appendUidMap(2, config1, nullptr, true, true, &proto);
UidMapping results;
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(1, results.snapshots_size());
// We have to keep at least one snapshot in memory at all times.
proto.clear();
- m.appendUidMap(2, config1, nullptr, &proto);
+ m.appendUidMap(2, config1, nullptr, true, true, &proto);
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(1, results.snapshots_size());
// Now add another configuration.
m.OnConfigUpdated(config2);
- m.updateApp(5, String16(kApp1.c_str()), 1000, 40);
+ m.updateApp(5, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16(""));
EXPECT_EQ(1U, m.mChanges.size());
proto.clear();
- m.appendUidMap(6, config1, nullptr, &proto);
+ m.appendUidMap(6, config1, nullptr, true, true, &proto);
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(1, results.snapshots_size());
EXPECT_EQ(1, results.changes_size());
EXPECT_EQ(1U, m.mChanges.size());
// Add another delta update.
- m.updateApp(7, String16(kApp2.c_str()), 1001, 41);
+ m.updateApp(7, String16(kApp2.c_str()), 1001, 41, String16("v41"), String16(""));
EXPECT_EQ(2U, m.mChanges.size());
// We still can't remove anything.
proto.clear();
- m.appendUidMap(8, config1, nullptr, &proto);
+ m.appendUidMap(8, config1, nullptr, true, true, &proto);
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(1, results.snapshots_size());
EXPECT_EQ(1, results.changes_size());
EXPECT_EQ(2U, m.mChanges.size());
proto.clear();
- m.appendUidMap(9, config2, nullptr, &proto);
+ m.appendUidMap(9, config2, nullptr, true, true, &proto);
protoOutputStreamToUidMapping(&proto, &results);
EXPECT_EQ(1, results.snapshots_size());
EXPECT_EQ(2, results.changes_size());
@@ -335,19 +367,23 @@
vector<int32_t> uids;
vector<int64_t> versions;
vector<String16> apps;
+ vector<String16> versionStrings;
+ vector<String16> installers;
uids.push_back(1000);
apps.push_back(String16(kApp1.c_str()));
versions.push_back(1);
- m.updateMap(1, uids, versions, apps);
+ versionStrings.push_back(String16("v1"));
+ installers.push_back(String16(""));
+ m.updateMap(1, uids, versions, versionStrings, apps, installers);
- m.updateApp(3, String16(kApp1.c_str()), 1000, 40);
+ m.updateApp(3, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16(""));
ProtoOutputStream proto;
vector<uint8_t> bytes;
- m.appendUidMap(2, config1, nullptr, &proto);
+ m.appendUidMap(2, config1, nullptr, true, true, &proto);
size_t prevBytes = m.mBytesUsed;
- m.appendUidMap(4, config1, nullptr, &proto);
+ m.appendUidMap(4, config1, nullptr, true, true, &proto);
EXPECT_TRUE(m.mBytesUsed < prevBytes);
}
@@ -361,21 +397,27 @@
size_t startBytes = m.mBytesUsed;
vector<int32_t> uids;
vector<int64_t> versions;
+ vector<String16> versionStrings;
+ vector<String16> installers;
vector<String16> apps;
for (int i = 0; i < 100; i++) {
uids.push_back(1);
buf = "EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY." + to_string(i);
apps.push_back(String16(buf.c_str()));
versions.push_back(1);
+ versionStrings.push_back(String16("v1"));
+ installers.push_back(String16(""));
}
- m.updateMap(1, uids, versions, apps);
+ m.updateMap(1, uids, versions, versionStrings, apps, installers);
- m.updateApp(3, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 2);
+ m.updateApp(3, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 2,
+ String16("v2"), String16(""));
EXPECT_EQ(1U, m.mChanges.size());
// Now force deletion by limiting the memory to hold one delta change.
- m.maxBytesOverride = 80; // Since the app string alone requires >45 characters.
- m.updateApp(5, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 4);
+ m.maxBytesOverride = 120; // Since the app string alone requires >45 characters.
+ m.updateApp(5, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 4,
+ String16("v4"), String16(""));
EXPECT_EQ(1U, m.mChanges.size());
}
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index 5c47af7..a9841c9 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -69,8 +69,10 @@
// Here it assumes that GMS core has two uids.
processor->getUidMap()->updateMap(
1, {222, 444, 111, 333}, {1, 1, 2, 2},
+ {String16("v1"), String16("v1"), String16("v2"), String16("v2")},
{String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"),
- String16("APP3")});
+ String16("APP3")},
+ {String16(""), String16(""), String16(""), String16("")});
// GMS core node is in the middle.
std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
@@ -215,8 +217,10 @@
// Here it assumes that GMS core has two uids.
processor->getUidMap()->updateMap(
1, {222, 444, 111, 333}, {1, 1, 2, 2},
+ {String16("v1"), String16("v1"), String16("v2"), String16("v2")},
{String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"),
- String16("APP3")});
+ String16("APP3")},
+ {String16(""), String16(""), String16(""), String16("")});
// GMS core node is in the middle.
std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 3cb553f..2b0285b 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -132,7 +132,8 @@
service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
// This is a new installation, so there shouldn't be a split (should be same as the without
// split case).
- service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2);
+ service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+ String16(""));
// Goes into the second bucket.
service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
@@ -145,11 +146,13 @@
SendConfig(service, MakeConfig());
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
- service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())});
+ service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+ {String16("")});
// Force the uidmap to update at timestamp 2.
service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
- service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2);
+ service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+ String16(""));
// Goes into the second bucket.
service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
@@ -168,7 +171,8 @@
SendConfig(service, MakeConfig());
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
- service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())});
+ service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+ {String16("")});
// Force the uidmap to update at timestamp 2.
service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
@@ -189,13 +193,14 @@
TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
StatsService service(nullptr);
// Partial buckets don't occur when app is first installed.
- service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
+ service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeValueMetricConfig(0));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2);
+ service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
+ String16("v2"), String16(""));
ConfigMetricsReport report =
GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
@@ -206,14 +211,15 @@
TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
StatsService service(nullptr);
// Partial buckets don't occur when app is first installed.
- service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
+ service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2);
+ service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
+ String16(""));
ConfigMetricsReport report =
GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
@@ -229,13 +235,14 @@
TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
StatsService service(nullptr);
// Partial buckets don't occur when app is first installed.
- service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
+ service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeGaugeMetricConfig(0));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2);
+ service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
+ String16("v2"), String16(""));
ConfigMetricsReport report =
GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
@@ -246,14 +253,15 @@
TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
StatsService service(nullptr);
// Partial buckets don't occur when app is first installed.
- service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
+ service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2);
+ service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
+ String16(""));
ConfigMetricsReport report =
GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 851e35b..39b327c 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1320,18 +1320,6 @@
Landroid/security/Credentials;->convertToPem([Ljava/security/cert/Certificate;)[B
Landroid/security/IKeyChainService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/security/IKeyChainService;
Landroid/security/IKeyChainService;->requestPrivateKey(Ljava/lang/String;)Ljava/lang/String;
-Landroid/security/IKeystoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/security/IKeystoreService;
-Landroid/security/IKeystoreService;->clear_uid(J)I
-Landroid/security/IKeystoreService;->del(Ljava/lang/String;I)I
-Landroid/security/IKeystoreService;->exist(Ljava/lang/String;I)I
-Landroid/security/IKeystoreService;->generateKey(Ljava/lang/String;Landroid/security/keymaster/KeymasterArguments;[BIILandroid/security/keymaster/KeyCharacteristics;)I
-Landroid/security/IKeystoreService;->get(Ljava/lang/String;I)[B
-Landroid/security/IKeystoreService;->getState(I)I
-Landroid/security/IKeystoreService;->insert(Ljava/lang/String;[BII)I
-Landroid/security/IKeystoreService;->is_hardware_backed(Ljava/lang/String;)I
-Landroid/security/IKeystoreService;->list(Ljava/lang/String;I)[Ljava/lang/String;
-Landroid/security/IKeystoreService;->reset()I
-Landroid/security/IKeystoreService;->ungrant(Ljava/lang/String;I)I
Landroid/security/keymaster/KeymasterBlobArgument;-><init>(ILandroid/os/Parcel;)V
Landroid/security/keymaster/KeymasterBlobArgument;-><init>(I[B)V
Landroid/security/keymaster/KeymasterBlobArgument;->blob:[B
@@ -1343,6 +1331,17 @@
Landroid/security/keymaster/KeymasterLongArgument;-><init>(IJ)V
Landroid/security/keymaster/KeymasterLongArgument;-><init>(ILandroid/os/Parcel;)V
Landroid/security/keymaster/KeymasterLongArgument;->value:J
+Landroid/security/keystore/IKeystoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/security/keystore/IKeystoreService;
+Landroid/security/keystore/IKeystoreService;->clear_uid(J)I
+Landroid/security/keystore/IKeystoreService;->del(Ljava/lang/String;I)I
+Landroid/security/keystore/IKeystoreService;->exist(Ljava/lang/String;I)I
+Landroid/security/keystore/IKeystoreService;->get(Ljava/lang/String;I)[B
+Landroid/security/keystore/IKeystoreService;->getState(I)I
+Landroid/security/keystore/IKeystoreService;->insert(Ljava/lang/String;[BII)I
+Landroid/security/keystore/IKeystoreService;->is_hardware_backed(Ljava/lang/String;)I
+Landroid/security/keystore/IKeystoreService;->list(Ljava/lang/String;I)[Ljava/lang/String;
+Landroid/security/keystore/IKeystoreService;->reset()I
+Landroid/security/keystore/IKeystoreService;->ungrant(Ljava/lang/String;I)I
Landroid/service/carrier/ICarrierMessagingCallback$Stub;-><init>()V
Landroid/service/carrier/ICarrierMessagingService;->filterSms(Landroid/service/carrier/MessagePdu;Ljava/lang/String;IILandroid/service/carrier/ICarrierMessagingCallback;)V
Landroid/service/dreams/IDreamManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/dreams/IDreamManager;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index bd9cf6d..362f4ae 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -88,6 +88,8 @@
ParceledListSlice getRecentNotifyingAppsForUser(int userId);
int getBlockedAppCount(int userId);
boolean areChannelsBypassingDnd();
+ int getAppsBypassingDndCount(int uid);
+ ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId);
// TODO: Remove this when callers have been migrated to the equivalent
// INotificationListener method.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 03eba7e..004417b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4335,6 +4335,16 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {android.os.IIdmap2} for managing idmap files (used by overlay
+ * packages).
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ public static final String IDMAP_SERVICE = "idmap";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link VrManager} for accessing the VR service.
*
* @see #getSystemService(String)
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 02f38a7..e9b2404 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -7508,7 +7508,7 @@
*
* @see #putExtra(String, String)
*/
- public String getStringExtra(String name) {
+ public @Nullable String getStringExtra(String name) {
return mExtras == null ? null : mExtras.getString(name);
}
@@ -7522,7 +7522,7 @@
*
* @see #putExtra(String, CharSequence)
*/
- public CharSequence getCharSequenceExtra(String name) {
+ public @Nullable CharSequence getCharSequenceExtra(String name) {
return mExtras == null ? null : mExtras.getCharSequence(name);
}
@@ -7536,7 +7536,7 @@
*
* @see #putExtra(String, Parcelable)
*/
- public <T extends Parcelable> T getParcelableExtra(String name) {
+ public @Nullable <T extends Parcelable> T getParcelableExtra(String name) {
return mExtras == null ? null : mExtras.<T>getParcelable(name);
}
@@ -7550,7 +7550,7 @@
*
* @see #putExtra(String, Parcelable[])
*/
- public Parcelable[] getParcelableArrayExtra(String name) {
+ public @Nullable Parcelable[] getParcelableArrayExtra(String name) {
return mExtras == null ? null : mExtras.getParcelableArray(name);
}
@@ -7565,7 +7565,7 @@
*
* @see #putParcelableArrayListExtra(String, ArrayList)
*/
- public <T extends Parcelable> ArrayList<T> getParcelableArrayListExtra(String name) {
+ public @Nullable <T extends Parcelable> ArrayList<T> getParcelableArrayListExtra(String name) {
return mExtras == null ? null : mExtras.<T>getParcelableArrayList(name);
}
@@ -7579,7 +7579,7 @@
*
* @see #putExtra(String, Serializable)
*/
- public Serializable getSerializableExtra(String name) {
+ public @Nullable Serializable getSerializableExtra(String name) {
return mExtras == null ? null : mExtras.getSerializable(name);
}
@@ -7594,7 +7594,7 @@
*
* @see #putIntegerArrayListExtra(String, ArrayList)
*/
- public ArrayList<Integer> getIntegerArrayListExtra(String name) {
+ public @Nullable ArrayList<Integer> getIntegerArrayListExtra(String name) {
return mExtras == null ? null : mExtras.getIntegerArrayList(name);
}
@@ -7609,7 +7609,7 @@
*
* @see #putStringArrayListExtra(String, ArrayList)
*/
- public ArrayList<String> getStringArrayListExtra(String name) {
+ public @Nullable ArrayList<String> getStringArrayListExtra(String name) {
return mExtras == null ? null : mExtras.getStringArrayList(name);
}
@@ -7624,7 +7624,7 @@
*
* @see #putCharSequenceArrayListExtra(String, ArrayList)
*/
- public ArrayList<CharSequence> getCharSequenceArrayListExtra(String name) {
+ public @Nullable ArrayList<CharSequence> getCharSequenceArrayListExtra(String name) {
return mExtras == null ? null : mExtras.getCharSequenceArrayList(name);
}
@@ -7638,7 +7638,7 @@
*
* @see #putExtra(String, boolean[])
*/
- public boolean[] getBooleanArrayExtra(String name) {
+ public @Nullable boolean[] getBooleanArrayExtra(String name) {
return mExtras == null ? null : mExtras.getBooleanArray(name);
}
@@ -7652,7 +7652,7 @@
*
* @see #putExtra(String, byte[])
*/
- public byte[] getByteArrayExtra(String name) {
+ public @Nullable byte[] getByteArrayExtra(String name) {
return mExtras == null ? null : mExtras.getByteArray(name);
}
@@ -7666,7 +7666,7 @@
*
* @see #putExtra(String, short[])
*/
- public short[] getShortArrayExtra(String name) {
+ public @Nullable short[] getShortArrayExtra(String name) {
return mExtras == null ? null : mExtras.getShortArray(name);
}
@@ -7680,7 +7680,7 @@
*
* @see #putExtra(String, char[])
*/
- public char[] getCharArrayExtra(String name) {
+ public @Nullable char[] getCharArrayExtra(String name) {
return mExtras == null ? null : mExtras.getCharArray(name);
}
@@ -7694,7 +7694,7 @@
*
* @see #putExtra(String, int[])
*/
- public int[] getIntArrayExtra(String name) {
+ public @Nullable int[] getIntArrayExtra(String name) {
return mExtras == null ? null : mExtras.getIntArray(name);
}
@@ -7708,7 +7708,7 @@
*
* @see #putExtra(String, long[])
*/
- public long[] getLongArrayExtra(String name) {
+ public @Nullable long[] getLongArrayExtra(String name) {
return mExtras == null ? null : mExtras.getLongArray(name);
}
@@ -7722,7 +7722,7 @@
*
* @see #putExtra(String, float[])
*/
- public float[] getFloatArrayExtra(String name) {
+ public @Nullable float[] getFloatArrayExtra(String name) {
return mExtras == null ? null : mExtras.getFloatArray(name);
}
@@ -7736,7 +7736,7 @@
*
* @see #putExtra(String, double[])
*/
- public double[] getDoubleArrayExtra(String name) {
+ public @Nullable double[] getDoubleArrayExtra(String name) {
return mExtras == null ? null : mExtras.getDoubleArray(name);
}
@@ -7750,7 +7750,7 @@
*
* @see #putExtra(String, String[])
*/
- public String[] getStringArrayExtra(String name) {
+ public @Nullable String[] getStringArrayExtra(String name) {
return mExtras == null ? null : mExtras.getStringArray(name);
}
@@ -7764,7 +7764,7 @@
*
* @see #putExtra(String, CharSequence[])
*/
- public CharSequence[] getCharSequenceArrayExtra(String name) {
+ public @Nullable CharSequence[] getCharSequenceArrayExtra(String name) {
return mExtras == null ? null : mExtras.getCharSequenceArray(name);
}
@@ -7778,7 +7778,7 @@
*
* @see #putExtra(String, Bundle)
*/
- public Bundle getBundleExtra(String name) {
+ public @Nullable Bundle getBundleExtra(String name) {
return mExtras == null ? null : mExtras.getBundle(name);
}
@@ -8584,7 +8584,7 @@
* @see #removeExtra
* @see #getStringExtra(String)
*/
- public @NonNull Intent putExtra(String name, String value) {
+ public @NonNull Intent putExtra(String name, @Nullable String value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8607,7 +8607,7 @@
* @see #removeExtra
* @see #getCharSequenceExtra(String)
*/
- public @NonNull Intent putExtra(String name, CharSequence value) {
+ public @NonNull Intent putExtra(String name, @Nullable CharSequence value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8630,7 +8630,7 @@
* @see #removeExtra
* @see #getParcelableExtra(String)
*/
- public @NonNull Intent putExtra(String name, Parcelable value) {
+ public @NonNull Intent putExtra(String name, @Nullable Parcelable value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8653,7 +8653,7 @@
* @see #removeExtra
* @see #getParcelableArrayExtra(String)
*/
- public @NonNull Intent putExtra(String name, Parcelable[] value) {
+ public @NonNull Intent putExtra(String name, @Nullable Parcelable[] value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8677,7 +8677,7 @@
* @see #getParcelableArrayListExtra(String)
*/
public @NonNull Intent putParcelableArrayListExtra(String name,
- ArrayList<? extends Parcelable> value) {
+ @Nullable ArrayList<? extends Parcelable> value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8700,7 +8700,8 @@
* @see #removeExtra
* @see #getIntegerArrayListExtra(String)
*/
- public @NonNull Intent putIntegerArrayListExtra(String name, ArrayList<Integer> value) {
+ public @NonNull Intent putIntegerArrayListExtra(String name,
+ @Nullable ArrayList<Integer> value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8723,7 +8724,7 @@
* @see #removeExtra
* @see #getStringArrayListExtra(String)
*/
- public @NonNull Intent putStringArrayListExtra(String name, ArrayList<String> value) {
+ public @NonNull Intent putStringArrayListExtra(String name, @Nullable ArrayList<String> value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8747,7 +8748,7 @@
* @see #getCharSequenceArrayListExtra(String)
*/
public @NonNull Intent putCharSequenceArrayListExtra(String name,
- ArrayList<CharSequence> value) {
+ @Nullable ArrayList<CharSequence> value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8770,7 +8771,7 @@
* @see #removeExtra
* @see #getSerializableExtra(String)
*/
- public @NonNull Intent putExtra(String name, Serializable value) {
+ public @NonNull Intent putExtra(String name, @Nullable Serializable value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8793,7 +8794,7 @@
* @see #removeExtra
* @see #getBooleanArrayExtra(String)
*/
- public @NonNull Intent putExtra(String name, boolean[] value) {
+ public @NonNull Intent putExtra(String name, @Nullable boolean[] value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8816,7 +8817,7 @@
* @see #removeExtra
* @see #getByteArrayExtra(String)
*/
- public @NonNull Intent putExtra(String name, byte[] value) {
+ public @NonNull Intent putExtra(String name, @Nullable byte[] value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8839,7 +8840,7 @@
* @see #removeExtra
* @see #getShortArrayExtra(String)
*/
- public @NonNull Intent putExtra(String name, short[] value) {
+ public @NonNull Intent putExtra(String name, @Nullable short[] value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8862,7 +8863,7 @@
* @see #removeExtra
* @see #getCharArrayExtra(String)
*/
- public @NonNull Intent putExtra(String name, char[] value) {
+ public @NonNull Intent putExtra(String name, @Nullable char[] value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8885,7 +8886,7 @@
* @see #removeExtra
* @see #getIntArrayExtra(String)
*/
- public @NonNull Intent putExtra(String name, int[] value) {
+ public @NonNull Intent putExtra(String name, @Nullable int[] value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8908,7 +8909,7 @@
* @see #removeExtra
* @see #getLongArrayExtra(String)
*/
- public @NonNull Intent putExtra(String name, long[] value) {
+ public @NonNull Intent putExtra(String name, @Nullable long[] value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8931,7 +8932,7 @@
* @see #removeExtra
* @see #getFloatArrayExtra(String)
*/
- public @NonNull Intent putExtra(String name, float[] value) {
+ public @NonNull Intent putExtra(String name, @Nullable float[] value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8954,7 +8955,7 @@
* @see #removeExtra
* @see #getDoubleArrayExtra(String)
*/
- public @NonNull Intent putExtra(String name, double[] value) {
+ public @NonNull Intent putExtra(String name, @Nullable double[] value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -8977,7 +8978,7 @@
* @see #removeExtra
* @see #getStringArrayExtra(String)
*/
- public @NonNull Intent putExtra(String name, String[] value) {
+ public @NonNull Intent putExtra(String name, @Nullable String[] value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -9000,7 +9001,7 @@
* @see #removeExtra
* @see #getCharSequenceArrayExtra(String)
*/
- public @NonNull Intent putExtra(String name, CharSequence[] value) {
+ public @NonNull Intent putExtra(String name, @Nullable CharSequence[] value) {
if (mExtras == null) {
mExtras = new Bundle();
}
@@ -9023,7 +9024,7 @@
* @see #removeExtra
* @see #getBundleExtra(String)
*/
- public @NonNull Intent putExtra(String name, Bundle value) {
+ public @NonNull Intent putExtra(String name, @Nullable Bundle value) {
if (mExtras == null) {
mExtras = new Bundle();
}
diff --git a/core/java/android/content/MimeTypeFilter.java b/core/java/android/content/MimeTypeFilter.java
new file mode 100644
index 0000000..1c26fd9
--- /dev/null
+++ b/core/java/android/content/MimeTypeFilter.java
@@ -0,0 +1,154 @@
+/*
+ * 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 android.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+
+/**
+ * Provides utility methods for matching MIME type filters used in ContentProvider.
+ *
+ * <p>Wildcards are allowed only instead of the entire type or subtype with a tree prefix.
+ * Eg. image\/*, *\/* is a valid filter and will match image/jpeg, but image/j* is invalid and
+ * it will not match image/jpeg. Suffixes and parameters are not supported, and they are treated
+ * as part of the subtype during matching. Neither type nor subtype can be empty.
+ *
+ * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike the formal
+ * RFC definitions. As a result, you should always write these elements with lower case letters,
+ * or use {@link android.content.Intent#normalizeMimeType} to ensure that they are converted to
+ * lower case.</em>
+ *
+ * <p>MIME types can be null or ill-formatted. In such case they won't match anything.
+ *
+ * <p>MIME type filters must be correctly formatted, or an exception will be thrown.
+ * Copied from support library.
+ * {@hide}
+ */
+public final class MimeTypeFilter {
+
+ private MimeTypeFilter() {
+ }
+
+ private static boolean mimeTypeAgainstFilter(
+ @NonNull String[] mimeTypeParts, @NonNull String[] filterParts) {
+ if (filterParts.length != 2) {
+ throw new IllegalArgumentException(
+ "Ill-formatted MIME type filter. Must be type/subtype.");
+ }
+ if (filterParts[0].isEmpty() || filterParts[1].isEmpty()) {
+ throw new IllegalArgumentException(
+ "Ill-formatted MIME type filter. Type or subtype empty.");
+ }
+ if (mimeTypeParts.length != 2) {
+ return false;
+ }
+ if (!"*".equals(filterParts[0])
+ && !filterParts[0].equals(mimeTypeParts[0])) {
+ return false;
+ }
+ if (!"*".equals(filterParts[1])
+ && !filterParts[1].equals(mimeTypeParts[1])) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Matches one nullable MIME type against one MIME type filter.
+ * @return True if the {@code mimeType} matches the {@code filter}.
+ */
+ public static boolean matches(@Nullable String mimeType, @NonNull String filter) {
+ if (mimeType == null) {
+ return false;
+ }
+
+ final String[] mimeTypeParts = mimeType.split("/");
+ final String[] filterParts = filter.split("/");
+
+ return mimeTypeAgainstFilter(mimeTypeParts, filterParts);
+ }
+
+ /**
+ * Matches one nullable MIME type against an array of MIME type filters.
+ * @return The first matching filter, or null if nothing matches.
+ */
+ @Nullable
+ public static String matches(
+ @Nullable String mimeType, @NonNull String[] filters) {
+ if (mimeType == null) {
+ return null;
+ }
+
+ final String[] mimeTypeParts = mimeType.split("/");
+ for (String filter : filters) {
+ final String[] filterParts = filter.split("/");
+ if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+ return filter;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Matches multiple MIME types against an array of MIME type filters.
+ * @return The first matching MIME type, or null if nothing matches.
+ */
+ @Nullable
+ public static String matches(
+ @Nullable String[] mimeTypes, @NonNull String filter) {
+ if (mimeTypes == null) {
+ return null;
+ }
+
+ final String[] filterParts = filter.split("/");
+ for (String mimeType : mimeTypes) {
+ final String[] mimeTypeParts = mimeType.split("/");
+ if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+ return mimeType;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Matches multiple MIME types against an array of MIME type filters.
+ * @return The list of matching MIME types, or empty array if nothing matches.
+ */
+ @NonNull
+ public static String[] matchesMany(
+ @Nullable String[] mimeTypes, @NonNull String filter) {
+ if (mimeTypes == null) {
+ return new String[] {};
+ }
+
+ final ArrayList<String> list = new ArrayList<>();
+ final String[] filterParts = filter.split("/");
+ for (String mimeType : mimeTypes) {
+ final String[] mimeTypeParts = mimeType.split("/");
+ if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+ list.add(mimeType);
+ }
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 5f23749..4371c77 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -58,6 +58,7 @@
public final class AssetManager implements AutoCloseable {
private static final String TAG = "AssetManager";
private static final boolean DEBUG_REFS = false;
+ private static final boolean FEATURE_FLAG_IDMAP2 = false;
private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
@@ -195,13 +196,23 @@
return;
}
- // Make sure that all IDMAPs are up to date.
- nativeVerifySystemIdmaps();
try {
final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/));
- loadStaticRuntimeOverlays(apkAssets);
+ if (FEATURE_FLAG_IDMAP2) {
+ final String[] systemIdmapPaths =
+ nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
+ if (systemIdmapPaths == null) {
+ throw new IOException("idmap2 scan failed");
+ }
+ for (String idmapPath : systemIdmapPaths) {
+ apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
+ }
+ } else {
+ nativeVerifySystemIdmaps();
+ loadStaticRuntimeOverlays(apkAssets);
+ }
sSystemApkAssetsSet = new ArraySet<>(apkAssets);
sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]);
@@ -1404,6 +1415,7 @@
private static native long nativeAssetGetRemainingLength(long assetPtr);
private static native void nativeVerifySystemIdmaps();
+ private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
// Global debug native methods.
/**
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 9cf7de5..c437dde 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2421,7 +2421,7 @@
public static final IntToString[] HISTORY_EVENT_INT_FORMATTERS = new IntToString[] {
sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sUidToString,
- sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sUidToString,
+ sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sIntToString,
sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sUidToString,
sUidToString, sUidToString, sUidToString, sIntToString
};
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 1f47f93..28ea553 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1390,3 +1390,4 @@
}
}
}
+
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 124f207..74d434c 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -62,12 +62,15 @@
* Inform statsd what the version and package are for each uid. Note that each array should
* have the same number of elements, and version[i] and package[i] correspond to uid[i].
*/
- oneway void informAllUidData(in int[] uid, in long[] version, in String[] app);
+ oneway void informAllUidData(in int[] uid, in long[] version, in String[] version_string,
+ in String[] app, in String[] installer);
/**
- * Inform statsd what the uid and version are for one app that was updated.
+ * Inform statsd what the uid, version, version_string, and installer are for one app that was
+ * updated.
*/
- oneway void informOnePackage(in String app, in int uid, in long version);
+ oneway void informOnePackage(in String app, in int uid, in long version,
+ in String version_string, in String installer);
/**
* Inform stats that an app was removed.
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 126588a..44b9e311 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -1056,6 +1056,9 @@
/**
* Internal class representing a remote status read by
* {@link ParcelFileDescriptor#readCommStatus(FileDescriptor, byte[])}.
+ *
+ * Warning: this must be kept in sync with ParcelFileDescriptorStatus at
+ * frameworks/native/libs/binder/Parcel.cpp
*/
private static class Status {
/** Special value indicating remote side died. */
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 72e1ab9..866bd9a 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -104,14 +104,6 @@
}
/**
- * Write a double value.
- */
- public void writeDouble(double val) {
- mTypes.add(EVENT_TYPE_DOUBLE);
- mValues.add(val);
- }
-
- /**
* Write a storage value.
*/
public void writeStorage(byte[] val) {
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 67e52aa..16d454d 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -16,12 +16,11 @@
package android.provider;
-import static android.system.OsConstants.SEEK_SET;
-
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
import static com.android.internal.util.Preconditions.checkCollectionNotEmpty;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.ContentProviderClient;
@@ -29,13 +28,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.MimeTypeFilter;
import android.content.pm.ResolveInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.ImageDecoder;
-import android.graphics.Matrix;
import android.graphics.Point;
import android.media.ExifInterface;
import android.net.Uri;
@@ -50,20 +48,13 @@
import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.storage.StorageVolume;
-import android.system.ErrnoException;
-import android.system.Os;
import android.util.DataUnit;
import android.util.Log;
-import android.util.Size;
-import libcore.io.IoUtils;
-
-import java.io.BufferedInputStream;
import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -113,6 +104,54 @@
public static final String EXTRA_TARGET_URI = "android.content.extra.TARGET_URI";
/**
+ * Key for {@link DocumentsProvider} to query display name is matched.
+ * The match of display name is partial matching and case-insensitive.
+ * Ex: The value is "o", the display name of the results will contain
+ * both "foo" and "Open".
+ *
+ * @see DocumentsProvider#querySearchDocuments(String, String[],
+ * Bundle)
+ * {@hide}
+ */
+ public static final String QUERY_ARG_DISPLAY_NAME = "android:query-arg-display-name";
+
+ /**
+ * Key for {@link DocumentsProvider} to query mime types is matched.
+ * The value is a string array, it can support different mime types.
+ * Each items will be treated as "OR" condition. Ex: {"image/*" ,
+ * "video/*"}. The mime types of the results will contain both image
+ * type and video type.
+ *
+ * @see DocumentsProvider#querySearchDocuments(String, String[],
+ * Bundle)
+ * {@hide}
+ */
+ public static final String QUERY_ARG_MIME_TYPES = "android:query-arg-mime-types";
+
+ /**
+ * Key for {@link DocumentsProvider} to query the file size in bytes is
+ * larger than the value.
+ *
+ * @see DocumentsProvider#querySearchDocuments(String, String[],
+ * Bundle)
+ * {@hide}
+ */
+ public static final String QUERY_ARG_FILE_SIZE_OVER = "android:query-arg-file-size-over";
+
+ /**
+ * Key for {@link DocumentsProvider} to query the last modified time
+ * is newer than the value. The unit is in milliseconds since
+ * January 1, 1970 00:00:00.0 UTC.
+ *
+ * @see DocumentsProvider#querySearchDocuments(String, String[],
+ * Bundle)
+ * @see Document#COLUMN_LAST_MODIFIED
+ * {@hide}
+ */
+ public static final String QUERY_ARG_LAST_MODIFIED_AFTER =
+ "android:query-arg-last-modified-after";
+
+ /**
* Sets the desired initial location visible to user when file chooser is shown.
*
* <p>Applicable to {@link Intent} with actions:
@@ -929,6 +968,89 @@
}
/**
+ * Check if the values match the query arguments.
+ *
+ * @param queryArgs the query arguments
+ * @param displayName the display time to check against
+ * @param mimeType the mime type to check against
+ * @param lastModified the last modified time to check against
+ * @param size the size to check against
+ * @hide
+ */
+ public static boolean matchSearchQueryArguments(Bundle queryArgs, String displayName,
+ String mimeType, long lastModified, long size) {
+ if (queryArgs == null) {
+ return true;
+ }
+
+ final String argDisplayName = queryArgs.getString(QUERY_ARG_DISPLAY_NAME, "");
+ if (!argDisplayName.isEmpty()) {
+ // TODO (118795812) : Enhance the search string handled in DocumentsProvider
+ if (!displayName.toLowerCase().contains(argDisplayName.toLowerCase())) {
+ return false;
+ }
+ }
+
+ final long argFileSize = queryArgs.getLong(QUERY_ARG_FILE_SIZE_OVER, -1 /* defaultValue */);
+ if (argFileSize != -1 && size < argFileSize) {
+ return false;
+ }
+
+ final long argLastModified = queryArgs.getLong(QUERY_ARG_LAST_MODIFIED_AFTER,
+ -1 /* defaultValue */);
+ if (argLastModified != -1 && lastModified < argLastModified) {
+ return false;
+ }
+
+ final String[] argMimeTypes = queryArgs.getStringArray(QUERY_ARG_MIME_TYPES);
+ if (argMimeTypes != null && argMimeTypes.length > 0) {
+ mimeType = Intent.normalizeMimeType(mimeType);
+ for (String type : argMimeTypes) {
+ if (MimeTypeFilter.matches(mimeType, Intent.normalizeMimeType(type))) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get the handled query arguments from the query bundle. The handled arguments are
+ * {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME},
+ * {@link DocumentsContract#QUERY_ARG_MIME_TYPES},
+ * {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER} and
+ * {@link DocumentsContract#QUERY_ARG_LAST_MODIFIED_AFTER}.
+ *
+ * @param queryArgs the query arguments to be parsed.
+ * @return the handled query arguments
+ * @hide
+ */
+ public static String[] getHandledQueryArguments(Bundle queryArgs) {
+ if (queryArgs == null) {
+ return new String[0];
+ }
+
+ final ArrayList<String> args = new ArrayList<>();
+ if (queryArgs.keySet().contains(QUERY_ARG_DISPLAY_NAME)) {
+ args.add(QUERY_ARG_DISPLAY_NAME);
+ }
+
+ if (queryArgs.keySet().contains(QUERY_ARG_FILE_SIZE_OVER)) {
+ args.add(QUERY_ARG_FILE_SIZE_OVER);
+ }
+
+ if (queryArgs.keySet().contains(QUERY_ARG_LAST_MODIFIED_AFTER)) {
+ args.add(QUERY_ARG_LAST_MODIFIED_AFTER);
+ }
+
+ if (queryArgs.keySet().contains(QUERY_ARG_MIME_TYPES)) {
+ args.add(QUERY_ARG_MIME_TYPES);
+ }
+ return args.toArray(new String[0]);
+ }
+
+ /**
* Test if the given URI represents a {@link Document} backed by a
* {@link DocumentsProvider}.
*
@@ -1052,6 +1174,15 @@
return searchDocumentsUri.getQueryParameter(PARAM_QUERY);
}
+ /**
+ * Extract the search query from a Bundle
+ * {@link #QUERY_ARG_DISPLAY_NAME}.
+ * {@hide}
+ */
+ public static String getSearchDocumentsQuery(@NonNull Bundle bundle) {
+ return bundle.getString(QUERY_ARG_DISPLAY_NAME, "" /* defaultValue */);
+ }
+
/** {@hide} */
@UnsupportedAppUsage
public static Uri setManageMode(Uri uri) {
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 68f8acd..58f8213 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -32,7 +32,6 @@
import static android.provider.DocumentsContract.buildTreeDocumentUri;
import static android.provider.DocumentsContract.getDocumentId;
import static android.provider.DocumentsContract.getRootId;
-import static android.provider.DocumentsContract.getSearchDocumentsQuery;
import static android.provider.DocumentsContract.getTreeDocumentId;
import static android.provider.DocumentsContract.isTreeUri;
@@ -47,6 +46,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.MimeTypeFilter;
import android.content.UriMatcher;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
@@ -651,6 +651,55 @@
}
/**
+ * Return documents that match the given query under the requested
+ * root. The returned documents should be sorted by relevance in descending
+ * order. How documents are matched against the query string is an
+ * implementation detail left to each provider, but it's suggested that at
+ * least {@link Document#COLUMN_DISPLAY_NAME} be matched in a
+ * case-insensitive fashion.
+ * <p>
+ * If your provider is cloud-based, and you have some data cached or pinned
+ * locally, you may return the local data immediately, setting
+ * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that
+ * you are still fetching additional data. Then, when the network data is
+ * available, you can send a change notification to trigger a requery and
+ * return the complete contents.
+ * <p>
+ * To support change notifications, you must
+ * {@link Cursor#setNotificationUri(ContentResolver, Uri)} with a relevant
+ * Uri, such as {@link DocumentsContract#buildSearchDocumentsUri(String,
+ * String, String)}. Then you can call {@link ContentResolver#notifyChange(Uri,
+ * android.database.ContentObserver, boolean)} with that Uri to send change
+ * notifications.
+ *
+ * @param rootId the root to search under.
+ * @param projection list of {@link Document} columns to put into the
+ * cursor. If {@code null} all supported columns should be
+ * included.
+ * @param queryArgs the query arguments.
+ * {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME},
+ * {@link DocumentsContract#QUERY_ARG_MIME_TYPES},
+ * {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER},
+ * {@link DocumentsContract#QUERY_ARG_LAST_MODIFIED_AFTER}.
+ * @return cursor containing search result. Include
+ * {@link ContentResolver#EXTRA_HONORED_ARGS} in {@link Cursor}
+ * extras {@link Bundle} when any QUERY_ARG_* value was honored
+ * during the preparation of the results.
+ *
+ * @see ContentResolver#EXTRA_HONORED_ARGS
+ * @see DocumentsContract#EXTRA_LOADING
+ * @see DocumentsContract#EXTRA_INFO
+ * @see DocumentsContract#EXTRA_ERROR
+ * {@hide}
+ */
+ @SuppressWarnings("unused")
+ public Cursor querySearchDocuments(String rootId, String[] projection, Bundle queryArgs)
+ throws FileNotFoundException {
+ return querySearchDocuments(rootId, DocumentsContract.getSearchDocumentsQuery(queryArgs),
+ projection);
+ }
+
+ /**
* Ejects the root. Throws {@link IllegalStateException} if ejection failed.
*
* @param rootId the root to be ejected.
@@ -795,7 +844,7 @@
* {@link #queryDocument(String, String[])},
* {@link #queryRecentDocuments(String, String[])},
* {@link #queryRoots(String[])}, and
- * {@link #querySearchDocuments(String, String, String[])}.
+ * {@link #querySearchDocuments(String, String[], Bundle)}.
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
@@ -812,7 +861,7 @@
* @see #queryRecentDocuments(String, String[], Bundle, CancellationSignal)
* @see #queryDocument(String, String[])
* @see #queryChildDocuments(String, String[], String)
- * @see #querySearchDocuments(String, String, String[])
+ * @see #querySearchDocuments(String, String[], Bundle)
*/
@Override
public final Cursor query(
@@ -825,8 +874,7 @@
return queryRecentDocuments(
getRootId(uri), projection, queryArgs, cancellationSignal);
case MATCH_SEARCH:
- return querySearchDocuments(
- getRootId(uri), getSearchDocumentsQuery(uri), projection);
+ return querySearchDocuments(getRootId(uri), projection, queryArgs);
case MATCH_DOCUMENT:
case MATCH_DOCUMENT_TREE:
enforceTree(uri);
@@ -1301,7 +1349,7 @@
final long flags =
cursor.getLong(cursor.getColumnIndexOrThrow(Document.COLUMN_FLAGS));
if ((flags & Document.FLAG_VIRTUAL_DOCUMENT) == 0 && mimeType != null &&
- mimeTypeMatches(mimeTypeFilter, mimeType)) {
+ MimeTypeFilter.matches(mimeType, mimeTypeFilter)) {
return new String[] { mimeType };
}
}
@@ -1354,21 +1402,4 @@
// For any other yet unhandled case, let the provider subclass handle it.
return openTypedDocument(documentId, mimeTypeFilter, opts, signal);
}
-
- /**
- * @hide
- */
- public static boolean mimeTypeMatches(String filter, String test) {
- if (test == null) {
- return false;
- } else if (filter == null || "*/*".equals(filter)) {
- return true;
- } else if (filter.equals(test)) {
- return true;
- } else if (filter.endsWith("/*")) {
- return filter.regionMatches(0, test, 0, filter.indexOf('/'));
- } else {
- return false;
- }
- }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0402222..b266648 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1825,53 +1825,6 @@
})
public @interface ResetMode{}
-
- /**
- * Indicates that the user has not started setup personalization.
- * One of the possible states for {@link Secure#USER_SETUP_PERSONALIZATION_STATE}.
- *
- * @hide
- */
- @SystemApi
- public static final int USER_SETUP_PERSONALIZATION_NOT_STARTED = 0;
-
- /**
- * Indicates that the user has not yet completed setup personalization.
- * One of the possible states for {@link Secure#USER_SETUP_PERSONALIZATION_STATE}.
- *
- * @hide
- */
- @SystemApi
- public static final int USER_SETUP_PERSONALIZATION_STARTED = 1;
-
- /**
- * Indicates that the user has snoozed personalization and will complete it later.
- * One of the possible states for {@link Secure#USER_SETUP_PERSONALIZATION_STATE}.
- *
- * @hide
- */
- @SystemApi
- public static final int USER_SETUP_PERSONALIZATION_PAUSED = 2;
-
- /**
- * Indicates that the user has completed setup personalization.
- * One of the possible states for {@link Secure#USER_SETUP_PERSONALIZATION_STATE}.
- *
- * @hide
- */
- @SystemApi
- public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- USER_SETUP_PERSONALIZATION_NOT_STARTED,
- USER_SETUP_PERSONALIZATION_STARTED,
- USER_SETUP_PERSONALIZATION_PAUSED,
- USER_SETUP_PERSONALIZATION_COMPLETE
- })
- public @interface UserSetupPersonalization {}
-
/**
* Activity Extra: Number of certificates
* <p>
@@ -5650,6 +5603,52 @@
public static final String USER_SETUP_COMPLETE = "user_setup_complete";
/**
+ * Indicates that the user has not started setup personalization.
+ * One of the possible states for {@link #USER_SETUP_PERSONALIZATION_STATE}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int USER_SETUP_PERSONALIZATION_NOT_STARTED = 0;
+
+ /**
+ * Indicates that the user has not yet completed setup personalization.
+ * One of the possible states for {@link #USER_SETUP_PERSONALIZATION_STATE}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int USER_SETUP_PERSONALIZATION_STARTED = 1;
+
+ /**
+ * Indicates that the user has snoozed personalization and will complete it later.
+ * One of the possible states for {@link #USER_SETUP_PERSONALIZATION_STATE}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int USER_SETUP_PERSONALIZATION_PAUSED = 2;
+
+ /**
+ * Indicates that the user has completed setup personalization.
+ * One of the possible states for {@link #USER_SETUP_PERSONALIZATION_STATE}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ USER_SETUP_PERSONALIZATION_NOT_STARTED,
+ USER_SETUP_PERSONALIZATION_STARTED,
+ USER_SETUP_PERSONALIZATION_PAUSED,
+ USER_SETUP_PERSONALIZATION_COMPLETE
+ })
+ public @interface UserSetupPersonalization {}
+
+ /**
* Defines the user's current state of device personalization.
* The possible states are defined in {@link UserSetupPersonalization}.
*
@@ -10325,6 +10324,18 @@
private static final Validator WIFI_PNO_FREQUENCY_CULLING_ENABLED_VALIDATOR =
BOOLEAN_VALIDATOR;
+ /**
+ * Setting to enable including recency information when determining pno network priorities.
+ * Disabled by default, and setting it to 1 will enable it.
+ * The value is boolean (0 or 1).
+ * @hide
+ */
+ public static final String WIFI_PNO_RECENCY_SORTING_ENABLED =
+ "wifi_pno_recency_sorting_enabled";
+
+ private static final Validator WIFI_PNO_RECENCY_SORTING_ENABLED_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
/**
* The maximum number of times we will retry a connection to an access
* point for which we have failed in acquiring an IP address from DHCP.
@@ -12800,6 +12811,8 @@
VALIDATORS.put(DEVICE_DEMO_MODE, BOOLEAN_VALIDATOR);
VALIDATORS.put(WIFI_PNO_FREQUENCY_CULLING_ENABLED,
WIFI_PNO_FREQUENCY_CULLING_ENABLED_VALIDATOR);
+ VALIDATORS.put(WIFI_PNO_RECENCY_SORTING_ENABLED,
+ WIFI_PNO_RECENCY_SORTING_ENABLED_VALIDATOR);
}
/**
diff --git a/core/java/android/rolecontrollerservice/IRoleControllerService.aidl b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
index 0000b9f..ac5be06 100644
--- a/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
+++ b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
@@ -30,4 +30,6 @@
in IRoleManagerCallback callback);
void onClearRoleHolders(in String roleName, in IRoleManagerCallback callback);
+
+ void onGrantDefaultRoles(in IRoleManagerCallback callback);
}
diff --git a/core/java/android/rolecontrollerservice/RoleControllerService.java b/core/java/android/rolecontrollerservice/RoleControllerService.java
index da11bca..44c45bb 100644
--- a/core/java/android/rolecontrollerservice/RoleControllerService.java
+++ b/core/java/android/rolecontrollerservice/RoleControllerService.java
@@ -89,6 +89,13 @@
RoleControllerService.this.onClearRoleHolders(roleName,
new RoleManagerCallbackDelegate(callback));
}
+
+ @Override
+ public void onGrantDefaultRoles(IRoleManagerCallback callback) {
+ Preconditions.checkNotNull(callback, "callback cannot be null");
+ RoleControllerService.this.onGrantDefaultRoles(
+ new RoleManagerCallbackDelegate(callback));
+ }
};
}
@@ -133,6 +140,16 @@
public abstract void onClearRoleHolders(@NonNull String roleName,
@NonNull RoleManagerCallback callback);
+ /**
+ * Called by system to grant default permissions and roles.
+ * <p>
+ * This is typically when creating a new user or upgrading either system or
+ * permission controller package
+ *
+ * @param callback the callback for whether this call is successful
+ */
+ public abstract void onGrantDefaultRoles(@NonNull RoleManagerCallback callback);
+
private static class RoleManagerCallbackDelegate implements RoleManagerCallback {
private IRoleManagerCallback mCallback;
diff --git a/core/java/android/security/keymaster/ExportResult.java b/core/java/android/security/keymaster/ExportResult.java
index c104671..1ab79fb 100644
--- a/core/java/android/security/keymaster/ExportResult.java
+++ b/core/java/android/security/keymaster/ExportResult.java
@@ -28,6 +28,11 @@
public final int resultCode;
public final byte[] exportData;
+ public ExportResult(int resultCode) {
+ this.resultCode = resultCode;
+ this.exportData = new byte[0];
+ }
+
@UnsupportedAppUsage
public static final Parcelable.Creator<ExportResult> CREATOR = new
Parcelable.Creator<ExportResult>() {
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.java b/core/java/android/security/keymaster/KeyCharacteristics.java
index 555863e..a4fe75d 100644
--- a/core/java/android/security/keymaster/KeyCharacteristics.java
+++ b/core/java/android/security/keymaster/KeyCharacteristics.java
@@ -52,6 +52,14 @@
readFromParcel(in);
}
+ /**
+ * Makes a shallow copy of other by copying the other's references to the KeymasterArguments
+ */
+ public void shallowCopyFrom(KeyCharacteristics other) {
+ this.swEnforced = other.swEnforced;
+ this.hwEnforced = other.hwEnforced;
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/security/keymaster/KeymasterCertificateChain.java b/core/java/android/security/keymaster/KeymasterCertificateChain.java
index 243b9fe..00a1a1c 100644
--- a/core/java/android/security/keymaster/KeymasterCertificateChain.java
+++ b/core/java/android/security/keymaster/KeymasterCertificateChain.java
@@ -54,6 +54,14 @@
readFromParcel(in);
}
+ /**
+ * Makes a shallow copy of other by copying the reference to the certificate chain list.
+ * @param other
+ */
+ public void shallowCopyFrom(KeymasterCertificateChain other) {
+ this.mCertificates = other.mCertificates;
+ }
+
public List<byte[]> getCertificates() {
return mCertificates;
}
diff --git a/core/java/android/security/keymaster/OperationResult.java b/core/java/android/security/keymaster/OperationResult.java
index 2943211..bc4f360 100644
--- a/core/java/android/security/keymaster/OperationResult.java
+++ b/core/java/android/security/keymaster/OperationResult.java
@@ -59,6 +59,10 @@
this.outParams = outParams;
}
+ public OperationResult(int resultCode) {
+ this(resultCode, null, 0, 0, null, null);
+ }
+
protected OperationResult(Parcel in) {
resultCode = in.readInt();
token = in.readStrongBinder();
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 7b638b4..6eb433a 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -201,7 +201,7 @@
}
}
- mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
+ mCharsValid = hasReplacement;
if (mCharsValid) {
if (mChars == null || mChars.length < mLen) {
@@ -815,7 +815,6 @@
} else {
final int delta = mStart;
if (mComputed == null) {
- // TODO: Enable measured getRunAdvance for ReplacementSpan and RTL text.
return wp.getRunAdvance(mText, delta + start, delta + end,
delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
} else {
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
new file mode 100644
index 0000000..043b31d
--- /dev/null
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.view;
+
+import android.annotation.UnsupportedAppUsage;
+import android.graphics.BaseRecordingCanvas;
+import android.graphics.CanvasProperty;
+import android.graphics.Paint;
+
+/**
+ * This class exists temporarily to workaround broken apps
+ *
+ * b/119066174
+ *
+ * @hide
+ */
+public abstract class DisplayListCanvas extends BaseRecordingCanvas {
+
+ /** @hide */
+ protected DisplayListCanvas(long nativeCanvas) {
+ super(nativeCanvas);
+ }
+
+ /** @hide */
+ @UnsupportedAppUsage
+ public abstract void drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top,
+ CanvasProperty<Float> right, CanvasProperty<Float> bottom, CanvasProperty<Float> rx,
+ CanvasProperty<Float> ry, CanvasProperty<Paint> paint);
+
+ /** @hide */
+ @UnsupportedAppUsage
+ public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
+ CanvasProperty<Float> radius, CanvasProperty<Paint> paint);
+}
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 9d31bd1..78ad0da 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -293,6 +293,12 @@
setTarget(canvas.mNode);
}
+ /** @hide */
+ @UnsupportedAppUsage
+ public void setTarget(DisplayListCanvas canvas) {
+ setTarget((RecordingCanvas) canvas);
+ }
+
private void setTarget(RenderNode node) {
checkMutable();
if (mTarget != null) {
diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
index c02fb32..b74600b 100644
--- a/core/java/android/view/intelligence/IntelligenceManager.java
+++ b/core/java/android/view/intelligence/IntelligenceManager.java
@@ -86,6 +86,13 @@
*/
public static final int STATE_ACTIVE = 2;
+ /**
+ * Session is disabled.
+ *
+ * @hide
+ */
+ public static final int STATE_DISABLED = 3;
+
private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
/**
@@ -166,13 +173,7 @@
public void send(int resultCode, Bundle resultData)
throws RemoteException {
synchronized (mLock) {
- if (resultCode > 0) {
- mState = STATE_ACTIVE;
- } else {
- // TODO(b/111276913): handle other cases like disabled by
- // service
- resetStateLocked();
- }
+ mState = resultCode;
if (VERBOSE) {
Log.v(TAG, "onActivityStarted() result: code=" + resultCode
+ ", id=" + mId
@@ -203,9 +204,13 @@
// Typically happens on system apps that are started before the system service
// is ready (like com.android.settings/.FallbackHome)
//TODO(b/111276913): try to ignore session while system is not ready / boot
- // not complete instead.
- Log.w(TAG, "Closing session for " + getActivityDebugNameLocked()
- + " after " + numberEvents + " delayed events");
+ // not complete instead. Similarly, the manager service should return right away
+ // when the user does not have a service set
+ if (VERBOSE) {
+ Log.v(TAG, "Closing session for " + getActivityDebugNameLocked()
+ + " after " + numberEvents + " delayed events and state "
+ + getStateAsString(mState));
+ }
// TODO(b/111276913): blacklist activity / use special flag to indicate that
// when it's launched again
resetStateLocked();
@@ -380,7 +385,7 @@
//TODO(b/111276913): properly implement by checking if it was explicitly disabled by
// service, or if service is not set
// (and probably renamign to isEnabledLocked()
- return mService != null;
+ return mService != null && mState != STATE_DISABLED;
}
/**
@@ -509,6 +514,8 @@
return "WAITING_FOR_SERVER";
case STATE_ACTIVE:
return "ACTIVE";
+ case STATE_DISABLED:
+ return "DISABLED";
default:
return "INVALID:" + state;
}
diff --git a/core/java/android/view/textclassifier/ModelFileManager.java b/core/java/android/view/textclassifier/ModelFileManager.java
index 896b516..8558a46 100644
--- a/core/java/android/view/textclassifier/ModelFileManager.java
+++ b/core/java/android/view/textclassifier/ModelFileManager.java
@@ -251,6 +251,9 @@
if (!mLanguageIndependent && model.mLanguageIndependent) {
return true;
}
+ if (mLanguageIndependent && !model.mLanguageIndependent) {
+ return false;
+ }
// A higher-version model is preferred.
if (mVersion > model.getVersion()) {
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index b754d84..eb35587 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -16,302 +16,22 @@
*/
package android.widget;
-import android.annotation.UnsupportedAppUsage;
-import android.app.AlertDialog;
import android.content.Context;
-import android.content.DialogInterface;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageItemInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PermissionGroupInfo;
-import android.content.pm.PermissionInfo;
import android.graphics.drawable.Drawable;
-import android.os.Parcel;
-import android.os.UserHandle;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import com.android.internal.R;
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
/**
- * This class contains the SecurityPermissions view implementation.
- * Initially the package's advanced or dangerous security permissions
- * are displayed under categorized
- * groups. Clicking on the additional permissions presents
- * extended information consisting of all groups and permissions.
- * To use this view define a LinearLayout or any ViewGroup and add this
- * view by instantiating AppSecurityPermissions and invoking getPermissionsView.
+ * Allows the device admin to show certain dialogs. Should be integrated into settings.
*
+ * @deprecated
* {@hide}
*/
+@Deprecated
public class AppSecurityPermissions {
- public static final int WHICH_NEW = 1<<2;
- public static final int WHICH_ALL = 0xffff;
-
- private final static String TAG = "AppSecurityPermissions";
- private final static boolean localLOGV = false;
- private final Context mContext;
- private final LayoutInflater mInflater;
- private final PackageManager mPm;
- private final Map<String, MyPermissionGroupInfo> mPermGroups
- = new HashMap<String, MyPermissionGroupInfo>();
- private final List<MyPermissionGroupInfo> mPermGroupsList
- = new ArrayList<MyPermissionGroupInfo>();
- private final PermissionGroupInfoComparator mPermGroupComparator =
- new PermissionGroupInfoComparator();
- private final PermissionInfoComparator mPermComparator = new PermissionInfoComparator();
- private final List<MyPermissionInfo> mPermsList = new ArrayList<MyPermissionInfo>();
- private final CharSequence mNewPermPrefix;
- private String mPackageName;
-
- /** @hide */
- static class MyPermissionGroupInfo extends PermissionGroupInfo {
- CharSequence mLabel;
-
- final ArrayList<MyPermissionInfo> mNewPermissions = new ArrayList<MyPermissionInfo>();
- final ArrayList<MyPermissionInfo> mAllPermissions = new ArrayList<MyPermissionInfo>();
-
- MyPermissionGroupInfo(PermissionInfo perm) {
- name = perm.packageName;
- packageName = perm.packageName;
- }
-
- MyPermissionGroupInfo(PermissionGroupInfo info) {
- super(info);
- }
-
- public Drawable loadGroupIcon(Context context, PackageManager pm) {
- if (icon != 0) {
- return loadUnbadgedIcon(pm);
- } else {
- return context.getDrawable(R.drawable.ic_perm_device_info);
- }
- }
- }
-
- /** @hide */
- private static class MyPermissionInfo extends PermissionInfo {
- CharSequence mLabel;
-
- /**
- * PackageInfo.requestedPermissionsFlags for the new package being installed.
- */
- int mNewReqFlags;
-
- /**
- * PackageInfo.requestedPermissionsFlags for the currently installed
- * package, if it is installed.
- */
- int mExistingReqFlags;
-
- /**
- * True if this should be considered a new permission.
- */
- boolean mNew;
-
- MyPermissionInfo(PermissionInfo info) {
- super(info);
- }
- }
-
- /** @hide */
- public static class PermissionItemView extends LinearLayout implements View.OnClickListener {
- MyPermissionGroupInfo mGroup;
- MyPermissionInfo mPerm;
- AlertDialog mDialog;
- private boolean mShowRevokeUI = false;
- private String mPackageName;
-
- public PermissionItemView(Context context, AttributeSet attrs) {
- super(context, attrs);
- setClickable(true);
- }
-
- public void setPermission(MyPermissionGroupInfo grp, MyPermissionInfo perm,
- boolean first, CharSequence newPermPrefix, String packageName,
- boolean showRevokeUI) {
- mGroup = grp;
- mPerm = perm;
- mShowRevokeUI = showRevokeUI;
- mPackageName = packageName;
-
- ImageView permGrpIcon = findViewById(R.id.perm_icon);
- TextView permNameView = findViewById(R.id.perm_name);
-
- PackageManager pm = getContext().getPackageManager();
- Drawable icon = null;
- if (first) {
- icon = grp.loadGroupIcon(getContext(), pm);
- }
- CharSequence label = perm.mLabel;
- if (perm.mNew && newPermPrefix != null) {
- // If this is a new permission, format it appropriately.
- SpannableStringBuilder builder = new SpannableStringBuilder();
- Parcel parcel = Parcel.obtain();
- TextUtils.writeToParcel(newPermPrefix, parcel, 0);
- parcel.setDataPosition(0);
- CharSequence newStr = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
- parcel.recycle();
- builder.append(newStr);
- builder.append(label);
- label = builder;
- }
-
- permGrpIcon.setImageDrawable(icon);
- permNameView.setText(label);
- setOnClickListener(this);
- if (localLOGV) Log.i(TAG, "Made perm item " + perm.name
- + ": " + label + " in group " + grp.name);
- }
-
- @Override
- public void onClick(View v) {
- if (mGroup != null && mPerm != null) {
- if (mDialog != null) {
- mDialog.dismiss();
- }
- PackageManager pm = getContext().getPackageManager();
- AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
- builder.setTitle(mGroup.mLabel);
- if (mPerm.descriptionRes != 0) {
- builder.setMessage(mPerm.loadDescription(pm));
- } else {
- CharSequence appName;
- try {
- ApplicationInfo app = pm.getApplicationInfo(mPerm.packageName, 0);
- appName = app.loadLabel(pm);
- } catch (NameNotFoundException e) {
- appName = mPerm.packageName;
- }
- StringBuilder sbuilder = new StringBuilder(128);
- sbuilder.append(getContext().getString(
- R.string.perms_description_app, appName));
- sbuilder.append("\n\n");
- sbuilder.append(mPerm.name);
- builder.setMessage(sbuilder.toString());
- }
- builder.setCancelable(true);
- builder.setIcon(mGroup.loadGroupIcon(getContext(), pm));
- addRevokeUIIfNecessary(builder);
- mDialog = builder.show();
- mDialog.setCanceledOnTouchOutside(true);
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mDialog != null) {
- mDialog.dismiss();
- }
- }
-
- private void addRevokeUIIfNecessary(AlertDialog.Builder builder) {
- if (!mShowRevokeUI) {
- return;
- }
-
- final boolean isRequired =
- ((mPerm.mExistingReqFlags & PackageInfo.REQUESTED_PERMISSION_REQUIRED) != 0);
-
- if (isRequired) {
- return;
- }
-
- DialogInterface.OnClickListener ocl = new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- PackageManager pm = getContext().getPackageManager();
- pm.revokeRuntimePermission(mPackageName, mPerm.name,
- new UserHandle(mContext.getUserId()));
- PermissionItemView.this.setVisibility(View.GONE);
- }
- };
- builder.setNegativeButton(R.string.revoke, ocl);
- builder.setPositiveButton(R.string.ok, null);
- }
- }
-
- private AppSecurityPermissions(Context context) {
- mContext = context;
- mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mPm = mContext.getPackageManager();
- // Pick up from framework resources instead.
- mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix);
- }
-
- @UnsupportedAppUsage
- public AppSecurityPermissions(Context context, String packageName) {
- this(context);
- mPackageName = packageName;
- Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
- PackageInfo pkgInfo;
- try {
- pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Couldn't retrieve permissions for package:"+packageName);
- return;
- }
- // Extract all user permissions
- if((pkgInfo.applicationInfo != null) && (pkgInfo.applicationInfo.uid != -1)) {
- getAllUsedPermissions(pkgInfo.applicationInfo.uid, permSet);
- }
- mPermsList.addAll(permSet);
- setPermissions(mPermsList);
- }
-
- public AppSecurityPermissions(Context context, PackageInfo info) {
- this(context);
- Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
- if(info == null) {
- return;
- }
- mPackageName = info.packageName;
-
- // Convert to a PackageInfo
- PackageInfo installedPkgInfo = null;
- // Get requested permissions
- if (info.requestedPermissions != null) {
- try {
- installedPkgInfo = mPm.getPackageInfo(info.packageName,
- PackageManager.GET_PERMISSIONS);
- } catch (NameNotFoundException e) {
- }
- extractPerms(info, permSet, installedPkgInfo);
- }
- // Get permissions related to shared user if any
- if (info.sharedUserId != null) {
- int sharedUid;
- try {
- sharedUid = mPm.getUidForSharedUser(info.sharedUserId);
- getAllUsedPermissions(sharedUid, permSet);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Couldn't retrieve shared user id for: " + info.packageName);
- }
- }
- // Retrieve list of permissions
- mPermsList.addAll(permSet);
- setPermissions(mPermsList);
- }
-
/**
* Utility to retrieve a view displaying a single permission. This provides
* the old UI layout for permissions; it is only here for the device admin
@@ -327,197 +47,6 @@
description, dangerous, icon);
}
- private void getAllUsedPermissions(int sharedUid, Set<MyPermissionInfo> permSet) {
- String sharedPkgList[] = mPm.getPackagesForUid(sharedUid);
- if(sharedPkgList == null || (sharedPkgList.length == 0)) {
- return;
- }
- for(String sharedPkg : sharedPkgList) {
- getPermissionsForPackage(sharedPkg, permSet);
- }
- }
-
- private void getPermissionsForPackage(String packageName, Set<MyPermissionInfo> permSet) {
- try {
- PackageInfo pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
- extractPerms(pkgInfo, permSet, pkgInfo);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Couldn't retrieve permissions for package: " + packageName);
- }
- }
-
- private void extractPerms(PackageInfo info, Set<MyPermissionInfo> permSet,
- PackageInfo installedPkgInfo) {
- String[] strList = info.requestedPermissions;
- int[] flagsList = info.requestedPermissionsFlags;
- if ((strList == null) || (strList.length == 0)) {
- return;
- }
- for (int i=0; i<strList.length; i++) {
- String permName = strList[i];
- try {
- PermissionInfo tmpPermInfo = mPm.getPermissionInfo(permName, 0);
- if (tmpPermInfo == null) {
- continue;
- }
- int existingIndex = -1;
- if (installedPkgInfo != null
- && installedPkgInfo.requestedPermissions != null) {
- for (int j=0; j<installedPkgInfo.requestedPermissions.length; j++) {
- if (permName.equals(installedPkgInfo.requestedPermissions[j])) {
- existingIndex = j;
- break;
- }
- }
- }
- final int existingFlags = existingIndex >= 0 ?
- installedPkgInfo.requestedPermissionsFlags[existingIndex] : 0;
- if (!isDisplayablePermission(tmpPermInfo, flagsList[i], existingFlags)) {
- // This is not a permission that is interesting for the user
- // to see, so skip it.
- continue;
- }
- final String origGroupName = tmpPermInfo.group;
- String groupName = origGroupName;
- if (groupName == null) {
- groupName = tmpPermInfo.packageName;
- tmpPermInfo.group = groupName;
- }
- MyPermissionGroupInfo group = mPermGroups.get(groupName);
- if (group == null) {
- PermissionGroupInfo grp = null;
- if (origGroupName != null) {
- grp = mPm.getPermissionGroupInfo(origGroupName, 0);
- }
- if (grp != null) {
- group = new MyPermissionGroupInfo(grp);
- } else {
- // We could be here either because the permission
- // didn't originally specify a group or the group it
- // gave couldn't be found. In either case, we consider
- // its group to be the permission's package name.
- tmpPermInfo.group = tmpPermInfo.packageName;
- group = mPermGroups.get(tmpPermInfo.group);
- if (group == null) {
- group = new MyPermissionGroupInfo(tmpPermInfo);
- }
- group = new MyPermissionGroupInfo(tmpPermInfo);
- }
- mPermGroups.put(tmpPermInfo.group, group);
- }
- final boolean newPerm = installedPkgInfo != null
- && (existingFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0;
- MyPermissionInfo myPerm = new MyPermissionInfo(tmpPermInfo);
- myPerm.mNewReqFlags = flagsList[i];
- myPerm.mExistingReqFlags = existingFlags;
- // This is a new permission if the app is already installed and
- // doesn't currently hold this permission.
- myPerm.mNew = newPerm;
- permSet.add(myPerm);
- } catch (NameNotFoundException e) {
- Log.i(TAG, "Ignoring unknown permission:"+permName);
- }
- }
- }
-
- @UnsupportedAppUsage
- public int getPermissionCount() {
- return getPermissionCount(WHICH_ALL);
- }
-
- private List<MyPermissionInfo> getPermissionList(MyPermissionGroupInfo grp, int which) {
- if (which == WHICH_NEW) {
- return grp.mNewPermissions;
- } else {
- return grp.mAllPermissions;
- }
- }
-
- public int getPermissionCount(int which) {
- int N = 0;
- for (int i=0; i<mPermGroupsList.size(); i++) {
- N += getPermissionList(mPermGroupsList.get(i), which).size();
- }
- return N;
- }
-
- @UnsupportedAppUsage
- public View getPermissionsView() {
- return getPermissionsView(WHICH_ALL, false);
- }
-
- public View getPermissionsViewWithRevokeButtons() {
- return getPermissionsView(WHICH_ALL, true);
- }
-
- public View getPermissionsView(int which) {
- return getPermissionsView(which, false);
- }
-
- private View getPermissionsView(int which, boolean showRevokeUI) {
- LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
- LinearLayout displayList = permsView.findViewById(R.id.perms_list);
- View noPermsView = permsView.findViewById(R.id.no_permissions);
-
- displayPermissions(mPermGroupsList, displayList, which, showRevokeUI);
- if (displayList.getChildCount() <= 0) {
- noPermsView.setVisibility(View.VISIBLE);
- }
-
- return permsView;
- }
-
- /**
- * Utility method that displays permissions from a map containing group name and
- * list of permission descriptions.
- */
- private void displayPermissions(List<MyPermissionGroupInfo> groups,
- LinearLayout permListView, int which, boolean showRevokeUI) {
- permListView.removeAllViews();
-
- int spacing = (int)(8*mContext.getResources().getDisplayMetrics().density);
-
- for (int i=0; i<groups.size(); i++) {
- MyPermissionGroupInfo grp = groups.get(i);
- final List<MyPermissionInfo> perms = getPermissionList(grp, which);
- for (int j=0; j<perms.size(); j++) {
- MyPermissionInfo perm = perms.get(j);
- View view = getPermissionItemView(grp, perm, j == 0,
- which != WHICH_NEW ? mNewPermPrefix : null, showRevokeUI);
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- if (j == 0) {
- lp.topMargin = spacing;
- }
- if (j == grp.mAllPermissions.size()-1) {
- lp.bottomMargin = spacing;
- }
- if (permListView.getChildCount() == 0) {
- lp.topMargin *= 2;
- }
- permListView.addView(view, lp);
- }
- }
- }
-
- private PermissionItemView getPermissionItemView(MyPermissionGroupInfo grp,
- MyPermissionInfo perm, boolean first, CharSequence newPermPrefix, boolean showRevokeUI) {
- return getPermissionItemView(mContext, mInflater, grp, perm, first, newPermPrefix,
- mPackageName, showRevokeUI);
- }
-
- private static PermissionItemView getPermissionItemView(Context context, LayoutInflater inflater,
- MyPermissionGroupInfo grp, MyPermissionInfo perm, boolean first,
- CharSequence newPermPrefix, String packageName, boolean showRevokeUI) {
- PermissionItemView permView = (PermissionItemView)inflater.inflate(
- (perm.flags & PermissionInfo.FLAG_COSTS_MONEY) != 0
- ? R.layout.app_permission_item_money : R.layout.app_permission_item,
- null);
- permView.setPermission(grp, perm, first, newPermPrefix, packageName, showRevokeUI);
- return permView;
- }
-
private static View getPermissionItemViewOld(Context context, LayoutInflater inflater,
CharSequence grpName, CharSequence permList, boolean dangerous, Drawable icon) {
View permView = inflater.inflate(R.layout.app_permission_item_old, null);
@@ -536,116 +65,4 @@
}
return permView;
}
-
- private boolean isDisplayablePermission(PermissionInfo pInfo, int newReqFlags,
- int existingReqFlags) {
- final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
- final boolean isNormal = (base == PermissionInfo.PROTECTION_NORMAL);
-
- // We do not show normal permissions in the UI.
- if (isNormal) {
- return false;
- }
-
- final boolean isDangerous = (base == PermissionInfo.PROTECTION_DANGEROUS)
- || ((pInfo.protectionLevel&PermissionInfo.PROTECTION_FLAG_PRE23) != 0);
- final boolean isRequired =
- ((newReqFlags&PackageInfo.REQUESTED_PERMISSION_REQUIRED) != 0);
- final boolean isDevelopment =
- ((pInfo.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0);
- final boolean wasGranted =
- ((existingReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0);
- final boolean isGranted =
- ((newReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0);
-
- // Dangerous and normal permissions are always shown to the user if the permission
- // is required, or it was previously granted
- if (isDangerous && (isRequired || wasGranted || isGranted)) {
- return true;
- }
-
- // Development permissions are only shown to the user if they are already
- // granted to the app -- if we are installing an app and they are not
- // already granted, they will not be granted as part of the install.
- if (isDevelopment && wasGranted) {
- if (localLOGV) Log.i(TAG, "Special perm " + pInfo.name
- + ": protlevel=0x" + Integer.toHexString(pInfo.protectionLevel));
- return true;
- }
- return false;
- }
-
- private static class PermissionGroupInfoComparator implements Comparator<MyPermissionGroupInfo> {
- private final Collator sCollator = Collator.getInstance();
- @Override
- public final int compare(MyPermissionGroupInfo a, MyPermissionGroupInfo b) {
- return sCollator.compare(a.mLabel, b.mLabel);
- }
- }
-
- private static class PermissionInfoComparator implements Comparator<MyPermissionInfo> {
- private final Collator sCollator = Collator.getInstance();
- PermissionInfoComparator() {
- }
- public final int compare(MyPermissionInfo a, MyPermissionInfo b) {
- return sCollator.compare(a.mLabel, b.mLabel);
- }
- }
-
- private void addPermToList(List<MyPermissionInfo> permList,
- MyPermissionInfo pInfo) {
- if (pInfo.mLabel == null) {
- pInfo.mLabel = pInfo.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
- | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
- }
- int idx = Collections.binarySearch(permList, pInfo, mPermComparator);
- if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+permList.size());
- if (idx < 0) {
- idx = -idx-1;
- permList.add(idx, pInfo);
- }
- }
-
- private void setPermissions(List<MyPermissionInfo> permList) {
- if (permList != null) {
- // First pass to group permissions
- for (MyPermissionInfo pInfo : permList) {
- if(localLOGV) Log.i(TAG, "Processing permission:"+pInfo.name);
- if(!isDisplayablePermission(pInfo, pInfo.mNewReqFlags, pInfo.mExistingReqFlags)) {
- if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable");
- continue;
- }
- MyPermissionGroupInfo group = mPermGroups.get(pInfo.group);
- if (group != null) {
- pInfo.mLabel = pInfo.loadSafeLabel(mPm, 20000,
- PackageItemInfo.SAFE_LABEL_FLAG_TRIM
- | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
- addPermToList(group.mAllPermissions, pInfo);
- if (pInfo.mNew) {
- addPermToList(group.mNewPermissions, pInfo);
- }
- }
- }
- }
-
- for (MyPermissionGroupInfo pgrp : mPermGroups.values()) {
- if (pgrp.labelRes != 0 || pgrp.nonLocalizedLabel != null) {
- pgrp.mLabel = pgrp.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
- | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
- } else {
- ApplicationInfo app;
- try {
- app = mPm.getApplicationInfo(pgrp.packageName, 0);
- pgrp.mLabel = app.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
- | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
- } catch (NameNotFoundException e) {
- pgrp.mLabel = pgrp.loadSafeLabel(mPm, 20000,
- PackageItemInfo.SAFE_LABEL_FLAG_TRIM
- | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
- }
- }
- mPermGroupsList.add(pgrp);
- }
- Collections.sort(mPermGroupsList, mPermGroupComparator);
- }
}
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 81dab2f..8bc90a8 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -389,14 +389,18 @@
* @param query the search condition used to match file names
* @param projection projection of the returned cursor
* @param exclusion absolute file paths to exclude from result
- * @return cursor containing search result
+ * @param queryArgs the query arguments for search
+ * @return cursor containing search result. Include
+ * {@link ContentResolver#EXTRA_HONORED_ARGS} in {@link Cursor}
+ * extras {@link Bundle} when any QUERY_ARG_* value was honored
+ * during the preparation of the results.
* @throws FileNotFoundException when root folder doesn't exist or search fails
+ *
+ * @see ContentResolver#EXTRA_HONORED_ARGS
*/
protected final Cursor querySearchDocuments(
- File folder, String query, String[] projection, Set<String> exclusion)
+ File folder, String[] projection, Set<String> exclusion, Bundle queryArgs)
throws FileNotFoundException {
-
- query = query.toLowerCase();
final MatrixCursor result = new MatrixCursor(resolveProjection(projection));
final LinkedList<File> pending = new LinkedList<>();
pending.add(folder);
@@ -407,11 +411,18 @@
pending.add(child);
}
}
- if (file.getName().toLowerCase().contains(query)
- && !exclusion.contains(file.getAbsolutePath())) {
+ if (!exclusion.contains(file.getAbsolutePath()) && matchSearchQueryArguments(file,
+ queryArgs)) {
includeFile(result, null, file);
}
}
+
+ final String[] handledQueryArgs = DocumentsContract.getHandledQueryArguments(queryArgs);
+ if (handledQueryArgs.length > 0) {
+ final Bundle extras = new Bundle();
+ extras.putStringArray(ContentResolver.EXTRA_HONORED_ARGS, handledQueryArgs);
+ result.setExtras(extras);
+ }
return result;
}
@@ -457,6 +468,34 @@
}
}
+ /**
+ * Test if the file matches the query arguments.
+ *
+ * @param file the file to test
+ * @param queryArgs the query arguments
+ */
+ private boolean matchSearchQueryArguments(File file, Bundle queryArgs) {
+ if (file == null) {
+ return false;
+ }
+
+ final String fileMimeType;
+ final String fileName = file.getName();
+
+ if (file.isDirectory()) {
+ fileMimeType = DocumentsContract.Document.MIME_TYPE_DIR;
+ } else {
+ int dotPos = fileName.lastIndexOf('.');
+ if (dotPos < 0) {
+ return false;
+ }
+ final String extension = fileName.substring(dotPos + 1);
+ fileMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+ }
+ return DocumentsContract.matchSearchQueryArguments(queryArgs, fileName, fileMimeType,
+ file.lastModified(), file.length());
+ }
+
private void scanFile(File visibleFile) {
final Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(visibleFile));
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 0baf73c..02c9542 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -130,6 +130,10 @@
public double wakeLockPowerMah;
public double wifiPowerMah;
+ // ****************
+ // This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto)
+ // so the ordinal values (and therefore the order) must never change.
+ // ****************
public enum DrainType {
AMBIENT_DISPLAY,
@UnsupportedAppUsage
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index b2d44e7..7b564ae 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -24,9 +24,13 @@
#include <sys/system_properties.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <unistd.h>
#include <private/android_filesystem_config.h> // for AID_SYSTEM
+#include <sstream>
+#include <string>
+
#include "android-base/logging.h"
#include "android-base/properties.h"
#include "android-base/stringprintf.h"
@@ -38,6 +42,7 @@
#include "androidfw/AssetManager2.h"
#include "androidfw/AttributeResolution.h"
#include "androidfw/MutexGuard.h"
+#include "androidfw/PosixUtils.h"
#include "androidfw/ResourceTypes.h"
#include "core_jni_helpers.h"
#include "jni.h"
@@ -54,6 +59,7 @@
extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
using ::android::base::StringPrintf;
+using ::android::util::ExecuteBinary;
namespace android {
@@ -161,18 +167,20 @@
argv[argc++] = AssetManager::IDMAP_DIR;
// Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
- // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
+ // use VENDOR_OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in
+ // addition to VENDOR_OVERLAY_DIR.
std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY,
"");
if (!overlay_theme_path.empty()) {
- overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path;
+ overlay_theme_path =
+ std::string(AssetManager::VENDOR_OVERLAY_DIR) + "/" + overlay_theme_path;
if (stat(overlay_theme_path.c_str(), &st) == 0) {
argv[argc++] = overlay_theme_path.c_str();
}
}
- if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
- argv[argc++] = AssetManager::OVERLAY_DIR;
+ if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
+ argv[argc++] = AssetManager::VENDOR_OVERLAY_DIR;
}
if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
@@ -200,6 +208,75 @@
}
}
+static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* env,
+ jclass /*clazz*/) {
+ // --input-directory can be given multiple times, but idmap2 expects the directory to exist
+ std::vector<std::string> input_dirs;
+ struct stat st;
+ if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
+ input_dirs.push_back(AssetManager::VENDOR_OVERLAY_DIR);
+ }
+
+ if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
+ input_dirs.push_back(AssetManager::PRODUCT_OVERLAY_DIR);
+ }
+
+ if (stat(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR, &st) == 0) {
+ input_dirs.push_back(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR);
+ }
+
+ if (input_dirs.empty()) {
+ LOG(WARNING) << "no directories for idmap2 to scan";
+ return env->NewObjectArray(0, g_stringClass, nullptr);
+ }
+
+ std::vector<std::string> argv{"/system/bin/idmap2",
+ "scan",
+ "--recursive",
+ "--target-package-name", "android",
+ "--target-apk-path", "/system/framework/framework-res.apk",
+ "--output-directory", "/data/resource-cache"};
+
+ for (const auto& dir : input_dirs) {
+ argv.push_back("--input-directory");
+ argv.push_back(dir);
+ }
+
+ const auto result = ExecuteBinary(argv);
+
+ if (!result) {
+ LOG(ERROR) << "failed to execute idmap2";
+ return nullptr;
+ }
+
+ if (result->status != 0) {
+ LOG(ERROR) << "idmap2: " << result->stderr;
+ return nullptr;
+ }
+
+ std::vector<std::string> idmap_paths;
+ std::istringstream input(result->stdout);
+ std::string path;
+ while (std::getline(input, path)) {
+ idmap_paths.push_back(path);
+ }
+
+ jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr);
+ if (array == nullptr) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < idmap_paths.size(); i++) {
+ const std::string path = idmap_paths[i];
+ jstring java_string = env->NewStringUTF(path.c_str());
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ env->SetObjectArrayElement(array, i, java_string);
+ env->DeleteLocalRef(java_string);
+ }
+ return array;
+}
+
static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
@@ -1405,6 +1482,8 @@
// System/idmap related methods.
{"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps},
+ {"nativeCreateIdmapsForStaticOverlaysTargetingAndroid", "()[Ljava/lang/String;",
+ (void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid},
// Global management/debug methods.
{"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 2465759..a398e49 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -85,7 +85,7 @@
// See AssetManager.cpp for more details on overlay-subdir.
static const char* kOverlayDir = "/system/vendor/overlay/";
static const char* kVendorOverlayDir = "/vendor/overlay";
- static const char* kOverlaySubdir = "/system/vendor/overlay-subdir/";
+ static const char* kVendorOverlaySubdir = "/system/vendor/overlay-subdir/";
static const char* kSystemProductOverlayDir = "/system/product/overlay/";
static const char* kProductOverlayDir = "/product/overlay";
static const char* kSystemProductServicesOverlayDir = "/system/product_services/overlay/";
@@ -93,7 +93,7 @@
static const char* kApkSuffix = ".apk";
if ((android::base::StartsWith(path, kOverlayDir)
- || android::base::StartsWith(path, kOverlaySubdir)
+ || android::base::StartsWith(path, kVendorOverlaySubdir)
|| android::base::StartsWith(path, kVendorOverlayDir)
|| android::base::StartsWith(path, kSystemProductOverlayDir)
|| android::base::StartsWith(path, kProductOverlayDir)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2976879..62896be 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -746,7 +746,7 @@
android:description="@string/permdesc_receiveMms"
android:protectionLevel="dangerous" />
- <!-- @TestApi Allows an application to read previously received cell broadcast
+ <!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast
messages and to register a content observer to get notifications when
a cell broadcast has been received and added to the database. For
emergency alerts, the database is updated immediately after the
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 26f3370..829d6f5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -55,6 +55,8 @@
<item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_microphone</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
</string-array>
@@ -87,6 +89,8 @@
<string translatable="false" name="status_bar_mobile">mobile</string>
<string translatable="false" name="status_bar_vpn">vpn</string>
<string translatable="false" name="status_bar_ethernet">ethernet</string>
+ <string translatable="false" name="status_bar_microphone">microphone</string>
+ <string translatable="false" name="status_bar_camera">camera</string>
<string translatable="false" name="status_bar_airplane">airplane</string>
<!-- Flag indicating whether the surface flinger has limited
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4eb723e..626206b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2808,6 +2808,8 @@
<java-symbol type="string" name="status_bar_mobile" />
<java-symbol type="string" name="status_bar_ethernet" />
<java-symbol type="string" name="status_bar_vpn" />
+ <java-symbol type="string" name="status_bar_microphone" />
+ <java-symbol type="string" name="status_bar_camera" />
<!-- Locale picker -->
<java-symbol type="id" name="locale_search_menu" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 8c91c37..002b6a8 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -494,6 +494,7 @@
Settings.Global.WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED,
Settings.Global.WIFI_LINK_SPEED_METRICS_ENABLED,
Settings.Global.WIFI_PNO_FREQUENCY_CULLING_ENABLED,
+ Settings.Global.WIFI_PNO_RECENCY_SORTING_ENABLED,
Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
Settings.Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
Settings.Global.WIFI_NETWORK_SHOW_RSSI,
diff --git a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
index f2efabf..88d162b 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
@@ -203,6 +203,28 @@
}
@Test
+ public void findBestModel_languageIsMoreImportantThanVersion_bestModelComesFirst() {
+ ModelFileManager.ModelFile matchLocaleModel =
+ new ModelFileManager.ModelFile(
+ new File("/path/b"), 1,
+ Collections.singletonList(Locale.forLanguageTag("ja")), false);
+
+ ModelFileManager.ModelFile languageIndependentModel =
+ new ModelFileManager.ModelFile(
+ new File("/path/a"), 2,
+ Collections.emptyList(), true);
+ when(mModelFileSupplier.get())
+ .thenReturn(
+ Arrays.asList(matchLocaleModel, languageIndependentModel));
+
+ ModelFileManager.ModelFile bestModelFile =
+ mModelFileManager.findBestModelFile(
+ LocaleList.forLanguageTags("ja"));
+
+ assertThat(bestModelFile).isEqualTo(matchLocaleModel);
+ }
+
+ @Test
public void modelFileEquals() {
ModelFileManager.ModelFile modelA =
new ModelFileManager.ModelFile(
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
index e81f678..7467114 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
@@ -41,6 +41,7 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.format.DateUtils;
+import android.util.StatsLog;
import junit.framework.TestCase;
@@ -258,6 +259,36 @@
assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS);
}
+ @Test
+ public void testDrainTypesSyncedWithProto() {
+ assertEquals(BatterySipper.DrainType.AMBIENT_DISPLAY.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__AMBIENT_DISPLAY);
+ // AtomsProto has no "APP"
+ assertEquals(BatterySipper.DrainType.BLUETOOTH.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__BLUETOOTH);
+ assertEquals(BatterySipper.DrainType.CAMERA.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CAMERA);
+ assertEquals(BatterySipper.DrainType.CELL.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CELL);
+ assertEquals(BatterySipper.DrainType.FLASHLIGHT.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__FLASHLIGHT);
+ assertEquals(BatterySipper.DrainType.IDLE.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__IDLE);
+ assertEquals(BatterySipper.DrainType.MEMORY.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__MEMORY);
+ assertEquals(BatterySipper.DrainType.OVERCOUNTED.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__OVERCOUNTED);
+ assertEquals(BatterySipper.DrainType.PHONE.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__PHONE);
+ assertEquals(BatterySipper.DrainType.SCREEN.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__SCREEN);
+ assertEquals(BatterySipper.DrainType.UNACCOUNTED.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__UNACCOUNTED);
+ // AtomsProto has no "USER"
+ assertEquals(BatterySipper.DrainType.WIFI.ordinal(),
+ StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__WIFI);
+ }
+
private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah,
int uidCode, boolean isUidNull) {
final BatterySipper sipper = mock(BatterySipper.class);
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index 936ad22..d24c140a 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -47,3 +47,11 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)
+
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.android.timezone.updater.xml
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_RELATIVE_PATH := permissions
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
diff --git a/data/etc/com.android.timezone.updater.xml b/data/etc/com.android.timezone.updater.xml
new file mode 100644
index 0000000..60a66e2
--- /dev/null
+++ b/data/etc/com.android.timezone.updater.xml
@@ -0,0 +1,22 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.timezone.updater">
+ <permission name="android.permission.QUERY_TIME_ZONE_RULES" />
+ <permission name="android.permission.UPDATE_TIME_ZONE_RULES" />
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 68f24fb..a4c5ed2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -173,6 +173,7 @@
<assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" />
<assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" />
+ <assign-permission name="android.permission.BATTERY_STATS" uid="statsd" />
<assign-permission name="android.permission.DUMP" uid="statsd" />
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="statsd" />
<assign-permission name="android.permission.STATSCOMPANION" uid="statsd" />
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index fd5d624..67ad404 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Pools.SynchronizedPool;
+import android.view.DisplayListCanvas;
import android.view.TextureLayer;
import dalvik.annotation.optimization.CriticalNative;
@@ -34,7 +35,7 @@
* {@link RenderNode#endRecording()} is called. It must not be retained beyond that as it is
* internally reused.
*/
-public final class RecordingCanvas extends BaseRecordingCanvas {
+public final class RecordingCanvas extends DisplayListCanvas {
// The recording canvas pool should be large enough to handle a deeply nested
// view hierarchy because display lists are generated recursively.
private static final int POOL_LIMIT = 25;
@@ -89,7 +90,8 @@
// Constructors
///////////////////////////////////////////////////////////////////////////
- private RecordingCanvas(@NonNull RenderNode node, int width, int height) {
+ /** @hide */
+ protected RecordingCanvas(@NonNull RenderNode node, int width, int height) {
super(nCreateDisplayListCanvas(node.mNativeRenderNode, width, height));
mDensity = 0; // disable bitmap density scaling
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index c10e482..835b735 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -38,11 +38,13 @@
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.OperationResult;
+import android.security.keystore.IKeystoreService;
import android.security.keystore.KeyExpiredException;
import android.security.keystore.KeyNotYetValidException;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
+import android.security.keystore.KeystoreResponse;
import android.security.keystore.StrongBoxUnavailableException;
import android.security.keystore.UserNotAuthenticatedException;
import android.util.Log;
@@ -52,8 +54,11 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
+import java.util.Arrays;
import java.util.List;
import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
import sun.security.util.ObjectIdentifier;
import sun.security.x509.AlgorithmId;
@@ -303,6 +308,31 @@
}
}
+ /**
+ * List uids of all keys that are auth bound to the current user.
+ * Only system is allowed to call this method.
+ */
+ @UnsupportedAppUsage
+ public int[] listUidsOfAuthBoundKeys() {
+ final int MAX_RESULT_SIZE = 100;
+ int[] uidsOut = new int[MAX_RESULT_SIZE];
+ try {
+ int rc = mBinder.listUidsOfAuthBoundKeys(uidsOut);
+ if (rc != NO_ERROR) {
+ Log.w(TAG, String.format("listUidsOfAuthBoundKeys failed with error code %d", rc));
+ return null;
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to keystore", e);
+ return null;
+ } catch (android.os.ServiceSpecificException e) {
+ Log.w(TAG, "KeyStore exception", e);
+ return null;
+ }
+ // Remove any 0 entries
+ return Arrays.stream(uidsOut).filter(x -> x > 0).toArray();
+ }
+
public String[] list(String prefix) {
return list(prefix, UID_SELF);
}
@@ -451,27 +481,107 @@
public boolean addRngEntropy(byte[] data, int flags) {
try {
- return mBinder.addRngEntropy(data, flags) == NO_ERROR;
+ KeystoreResultPromise promise = new KeystoreResultPromise();
+ int errorCode = mBinder.addRngEntropy(promise, data, flags);
+ if (errorCode == NO_ERROR) {
+ return promise.getFuture().get().getErrorCode() == NO_ERROR;
+ } else {
+ return false;
+ }
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
+ } catch (ExecutionException | InterruptedException e) {
+ Log.e(TAG, "AddRngEntropy completed with exception", e);
+ return false;
}
}
+ private class KeyCharacteristicsCallbackResult {
+ private KeystoreResponse keystoreResponse;
+ private KeyCharacteristics keyCharacteristics;
+
+ public KeyCharacteristicsCallbackResult(KeystoreResponse keystoreResponse,
+ KeyCharacteristics keyCharacteristics) {
+ this.keystoreResponse = keystoreResponse;
+ this.keyCharacteristics = keyCharacteristics;
+ }
+
+ public KeystoreResponse getKeystoreResponse() {
+ return keystoreResponse;
+ }
+
+ public void setKeystoreResponse(KeystoreResponse keystoreResponse) {
+ this.keystoreResponse = keystoreResponse;
+ }
+
+ public KeyCharacteristics getKeyCharacteristics() {
+ return keyCharacteristics;
+ }
+
+ public void setKeyCharacteristics(KeyCharacteristics keyCharacteristics) {
+ this.keyCharacteristics = keyCharacteristics;
+ }
+ }
+
+ private class KeyCharacteristicsPromise
+ extends android.security.keystore.IKeystoreKeyCharacteristicsCallback.Stub {
+ final private CompletableFuture<KeyCharacteristicsCallbackResult> future =
+ new CompletableFuture<KeyCharacteristicsCallbackResult>();
+ @Override
+ public void onFinished(KeystoreResponse keystoreResponse,
+ KeyCharacteristics keyCharacteristics)
+ throws android.os.RemoteException {
+ future.complete(
+ new KeyCharacteristicsCallbackResult(keystoreResponse, keyCharacteristics));
+ }
+ public final CompletableFuture<KeyCharacteristicsCallbackResult> getFuture() {
+ return future;
+ }
+ };
+
+ private int generateKeyInternal(String alias, KeymasterArguments args, byte[] entropy, int uid,
+ int flags, KeyCharacteristics outCharacteristics)
+ throws RemoteException, ExecutionException, InterruptedException {
+ KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
+ int error = mBinder.generateKey(promise, alias, args, entropy, uid, flags);
+ if (error != NO_ERROR) {
+ Log.e(TAG, "generateKeyInternal failed on request " + error);
+ return error;
+ }
+
+ KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+ error = result.getKeystoreResponse().getErrorCode();
+ if (error != NO_ERROR) {
+ Log.e(TAG, "generateKeyInternal failed on response " + error);
+ return error;
+ }
+ KeyCharacteristics characteristics = result.getKeyCharacteristics();
+ if (characteristics == null) {
+ Log.e(TAG, "generateKeyInternal got empty key cheractariestics " + error);
+ return SYSTEM_ERROR;
+ }
+ outCharacteristics.shallowCopyFrom(characteristics);
+ return NO_ERROR;
+ }
+
public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int uid,
int flags, KeyCharacteristics outCharacteristics) {
try {
entropy = entropy != null ? entropy : new byte[0];
args = args != null ? args : new KeymasterArguments();
- int error = mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics);
+ int error = generateKeyInternal(alias, args, entropy, uid, flags, outCharacteristics);
if (error == KEY_ALREADY_EXISTS) {
mBinder.del(alias, uid);
- error = mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics);
+ error = generateKeyInternal(alias, args, entropy, uid, flags, outCharacteristics);
}
return error;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
+ } catch (ExecutionException | InterruptedException e) {
+ Log.e(TAG, "generateKey completed with exception", e);
+ return SYSTEM_ERROR;
}
}
@@ -485,10 +595,24 @@
try {
clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
- return mBinder.getKeyCharacteristics(alias, clientId, appId, uid, outCharacteristics);
+ KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
+ int error = mBinder.getKeyCharacteristics(promise, alias, clientId, appId, uid);
+ if (error != NO_ERROR) return error;
+
+ KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+ error = result.getKeystoreResponse().getErrorCode();
+ if (error != NO_ERROR) return error;
+
+ KeyCharacteristics characteristics = result.getKeyCharacteristics();
+ if (characteristics == null) return SYSTEM_ERROR;
+ outCharacteristics.shallowCopyFrom(characteristics);
+ return NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
+ } catch (ExecutionException | InterruptedException e) {
+ Log.e(TAG, "GetKeyCharacteristics completed with exception", e);
+ return SYSTEM_ERROR;
}
}
@@ -497,20 +621,40 @@
return getKeyCharacteristics(alias, clientId, appId, UID_SELF, outCharacteristics);
}
+ private int importKeyInternal(String alias, KeymasterArguments args, int format, byte[] keyData,
+ int uid, int flags, KeyCharacteristics outCharacteristics)
+ throws RemoteException, ExecutionException, InterruptedException {
+ KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
+ int error = mBinder.importKey(promise, alias, args, format, keyData, uid, flags);
+ if (error != NO_ERROR) return error;
+
+ KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+ error = result.getKeystoreResponse().getErrorCode();
+ if (error != NO_ERROR) return error;
+
+ KeyCharacteristics characteristics = result.getKeyCharacteristics();
+ if (characteristics == null) return SYSTEM_ERROR;
+ outCharacteristics.shallowCopyFrom(characteristics);
+ return NO_ERROR;
+ }
+
public int importKey(String alias, KeymasterArguments args, int format, byte[] keyData,
int uid, int flags, KeyCharacteristics outCharacteristics) {
try {
- int error = mBinder.importKey(alias, args, format, keyData, uid, flags,
+ int error = importKeyInternal(alias, args, format, keyData, uid, flags,
outCharacteristics);
if (error == KEY_ALREADY_EXISTS) {
mBinder.del(alias, uid);
- error = mBinder.importKey(alias, args, format, keyData, uid, flags,
+ error = importKeyInternal(alias, args, format, keyData, uid, flags,
outCharacteristics);
}
return error;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
+ } catch (ExecutionException | InterruptedException e) {
+ Log.e(TAG, "ImportKey completed with exception", e);
+ return SYSTEM_ERROR;
}
}
@@ -578,34 +722,79 @@
return true;
}
+ private int importWrappedKeyInternal(String wrappedKeyAlias, byte[] wrappedKey,
+ String wrappingKeyAlias,
+ byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid,
+ KeyCharacteristics outCharacteristics)
+ throws RemoteException, ExecutionException, InterruptedException {
+ KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
+ int error = mBinder.importWrappedKey(promise, wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
+ maskingKey, args, rootSid, fingerprintSid);
+ if (error != NO_ERROR) return error;
+
+ KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+ error = result.getKeystoreResponse().getErrorCode();
+ if (error != NO_ERROR) return error;
+
+ KeyCharacteristics characteristics = result.getKeyCharacteristics();
+ if (characteristics == null) return SYSTEM_ERROR;
+ outCharacteristics.shallowCopyFrom(characteristics);
+ return NO_ERROR;
+ }
+
public int importWrappedKey(String wrappedKeyAlias, byte[] wrappedKey,
String wrappingKeyAlias,
byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid, int uid,
KeyCharacteristics outCharacteristics) {
+ // TODO b/119217337 uid parameter gets silently ignored.
try {
- int error = mBinder.importWrappedKey(wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
+ int error = importWrappedKeyInternal(wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
maskingKey, args, rootSid, fingerprintSid, outCharacteristics);
if (error == KEY_ALREADY_EXISTS) {
- mBinder.del(wrappedKeyAlias, -1);
- error = mBinder.importWrappedKey(wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
+ mBinder.del(wrappedKeyAlias, UID_SELF);
+ error = importWrappedKeyInternal(wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
maskingKey, args, rootSid, fingerprintSid, outCharacteristics);
}
return error;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
+ } catch (ExecutionException | InterruptedException e) {
+ Log.e(TAG, "ImportWrappedKey completed with exception", e);
+ return SYSTEM_ERROR;
}
}
+ private class ExportKeyPromise
+ extends android.security.keystore.IKeystoreExportKeyCallback.Stub {
+ final private CompletableFuture<ExportResult> future = new CompletableFuture<ExportResult>();
+ @Override
+ public void onFinished(ExportResult exportKeyResult) throws android.os.RemoteException {
+ future.complete(exportKeyResult);
+ }
+ public final CompletableFuture<ExportResult> getFuture() {
+ return future;
+ }
+ };
+
public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
KeymasterBlob appId, int uid) {
try {
clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
- return mBinder.exportKey(alias, format, clientId, appId, uid);
+ ExportKeyPromise promise = new ExportKeyPromise();
+ int error = mBinder.exportKey(promise, alias, format, clientId, appId, uid);
+ if (error == NO_ERROR) {
+ return promise.getFuture().get();
+ } else {
+ return new ExportResult(error);
+ }
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
+ } catch (ExecutionException | InterruptedException e) {
+ Log.e(TAG, "ExportKey completed with exception", e);
+ return null;
}
}
public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
@@ -613,15 +802,37 @@
return exportKey(alias, format, clientId, appId, UID_SELF);
}
+ private class OperationPromise
+ extends android.security.keystore.IKeystoreOperationResultCallback.Stub {
+ final private CompletableFuture<OperationResult> future = new CompletableFuture<OperationResult>();
+ @Override
+ public void onFinished(OperationResult operationResult) throws android.os.RemoteException {
+ future.complete(operationResult);
+ }
+ public final CompletableFuture<OperationResult> getFuture() {
+ return future;
+ }
+ };
+
public OperationResult begin(String alias, int purpose, boolean pruneable,
KeymasterArguments args, byte[] entropy, int uid) {
try {
args = args != null ? args : new KeymasterArguments();
entropy = entropy != null ? entropy : new byte[0];
- return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid);
+ OperationPromise promise = new OperationPromise();
+ int errorCode = mBinder.begin(promise, getToken(), alias, purpose, pruneable, args,
+ entropy, uid);
+ if (errorCode == NO_ERROR) {
+ return promise.getFuture().get();
+ } else {
+ return new OperationResult(errorCode);
+ }
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
+ } catch (ExecutionException | InterruptedException e) {
+ Log.e(TAG, "Begin completed with exception", e);
+ return null;
}
}
@@ -636,10 +847,19 @@
try {
arguments = arguments != null ? arguments : new KeymasterArguments();
input = input != null ? input : new byte[0];
- return mBinder.update(token, arguments, input);
+ OperationPromise promise = new OperationPromise();
+ int errorCode = mBinder.update(promise, token, arguments, input);
+ if (errorCode == NO_ERROR) {
+ return promise.getFuture().get();
+ } else {
+ return new OperationResult(errorCode);
+ }
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
+ } catch (ExecutionException | InterruptedException e) {
+ Log.e(TAG, "Update completed with exception", e);
+ return null;
}
}
@@ -649,10 +869,19 @@
arguments = arguments != null ? arguments : new KeymasterArguments();
entropy = entropy != null ? entropy : new byte[0];
signature = signature != null ? signature : new byte[0];
- return mBinder.finish(token, arguments, signature, entropy);
+ OperationPromise promise = new OperationPromise();
+ int errorCode = mBinder.finish(promise, token, arguments, signature, entropy);
+ if (errorCode == NO_ERROR) {
+ return promise.getFuture().get();
+ } else {
+ return new OperationResult(errorCode);
+ }
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
+ } catch (ExecutionException | InterruptedException e) {
+ Log.e(TAG, "Finish completed with exception", e);
+ return null;
}
}
@@ -660,12 +889,33 @@
return finish(token, arguments, signature, null);
}
+ private class KeystoreResultPromise
+ extends android.security.keystore.IKeystoreResponseCallback.Stub {
+ final private CompletableFuture<KeystoreResponse> future = new CompletableFuture<KeystoreResponse>();
+ @Override
+ public void onFinished(KeystoreResponse keystoreResponse) throws android.os.RemoteException {
+ future.complete(keystoreResponse);
+ }
+ public final CompletableFuture<KeystoreResponse> getFuture() {
+ return future;
+ }
+ };
+
public int abort(IBinder token) {
try {
- return mBinder.abort(token);
+ KeystoreResultPromise promise = new KeystoreResultPromise();
+ int errorCode = mBinder.abort(promise, token);
+ if (errorCode == NO_ERROR) {
+ return promise.getFuture().get().getErrorCode();
+ } else {
+ return errorCode;
+ }
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
+ } catch (ExecutionException | InterruptedException e) {
+ Log.e(TAG, "Abort completed with exception", e);
+ return SYSTEM_ERROR;
}
}
@@ -747,6 +997,47 @@
return onUserPasswordChanged(UserHandle.getUserId(Process.myUid()), newPassword);
}
+ private class KeyAttestationCallbackResult {
+ private KeystoreResponse keystoreResponse;
+ private KeymasterCertificateChain certificateChain;
+
+ public KeyAttestationCallbackResult(KeystoreResponse keystoreResponse,
+ KeymasterCertificateChain certificateChain) {
+ this.keystoreResponse = keystoreResponse;
+ this.certificateChain = certificateChain;
+ }
+
+ public KeystoreResponse getKeystoreResponse() {
+ return keystoreResponse;
+ }
+
+ public void setKeystoreResponse(KeystoreResponse keystoreResponse) {
+ this.keystoreResponse = keystoreResponse;
+ }
+
+ public KeymasterCertificateChain getCertificateChain() {
+ return certificateChain;
+ }
+
+ public void setCertificateChain(KeymasterCertificateChain certificateChain) {
+ this.certificateChain = certificateChain;
+ }
+ }
+
+ private class CertificateChainPromise
+ extends android.security.keystore.IKeystoreCertificateChainCallback.Stub {
+ final private CompletableFuture<KeyAttestationCallbackResult> future = new CompletableFuture<KeyAttestationCallbackResult>();
+ @Override
+ public void onFinished(KeystoreResponse keystoreResponse,
+ KeymasterCertificateChain certificateChain) throws android.os.RemoteException {
+ future.complete(new KeyAttestationCallbackResult(keystoreResponse, certificateChain));
+ }
+ public final CompletableFuture<KeyAttestationCallbackResult> getFuture() {
+ return future;
+ }
+ };
+
+
public int attestKey(
String alias, KeymasterArguments params, KeymasterCertificateChain outChain) {
try {
@@ -756,10 +1047,21 @@
if (outChain == null) {
outChain = new KeymasterCertificateChain();
}
- return mBinder.attestKey(alias, params, outChain);
+ CertificateChainPromise promise = new CertificateChainPromise();
+ int error = mBinder.attestKey(promise, alias, params);
+ if (error != NO_ERROR) return error;
+ KeyAttestationCallbackResult result = promise.getFuture().get();
+ error = result.getKeystoreResponse().getErrorCode();
+ if (error == NO_ERROR) {
+ outChain.shallowCopyFrom(result.getCertificateChain());
+ }
+ return error;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
+ } catch (ExecutionException | InterruptedException e) {
+ Log.e(TAG, "AttestKey completed with exception", e);
+ return SYSTEM_ERROR;
}
}
@@ -771,10 +1073,21 @@
if (outChain == null) {
outChain = new KeymasterCertificateChain();
}
- return mBinder.attestDeviceIds(params, outChain);
+ CertificateChainPromise promise = new CertificateChainPromise();
+ int error = mBinder.attestDeviceIds(promise, params);
+ if (error != NO_ERROR) return error;
+ KeyAttestationCallbackResult result = promise.getFuture().get();
+ error = result.getKeystoreResponse().getErrorCode();
+ if (error == NO_ERROR) {
+ outChain.shallowCopyFrom(result.getCertificateChain());
+ }
+ return error;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
+ } catch (ExecutionException | InterruptedException e) {
+ Log.e(TAG, "AttestDevicdeIds completed with exception", e);
+ return SYSTEM_ERROR;
}
}
diff --git a/keystore/java/android/security/keystore/KeystoreResponse.java b/keystore/java/android/security/keystore/KeystoreResponse.java
new file mode 100644
index 0000000..3a229cb
--- /dev/null
+++ b/keystore/java/android/security/keystore/KeystoreResponse.java
@@ -0,0 +1,78 @@
+/*
+ * 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 android.security.keystore;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelFormatException;
+
+/**
+ * The Java side of the KeystoreResponse.
+ * <p>
+ * Serialization code for this and subclasses must be kept in sync with system/security/keystore.
+ * @hide
+ */
+public class KeystoreResponse implements Parcelable {
+ public final int error_code_;
+ public final String error_msg_;
+
+ public static final Parcelable.Creator<KeystoreResponse> CREATOR = new
+ Parcelable.Creator<KeystoreResponse>() {
+ @Override
+ public KeystoreResponse createFromParcel(Parcel in) {
+ final int error_code = in.readInt();
+ final String error_msg = in.readString();
+ return new KeystoreResponse(error_code, error_msg);
+ }
+
+ @Override
+ public KeystoreResponse[] newArray(int size) {
+ return new KeystoreResponse[size];
+ }
+ };
+
+ protected KeystoreResponse(int error_code, String error_msg) {
+ this.error_code_ = error_code;
+ this.error_msg_ = error_msg;
+ }
+
+ /**
+ * @return the error_code_
+ */
+ public final int getErrorCode() {
+ return error_code_;
+ }
+
+ /**
+ * @return the error_msg_
+ */
+ public final String getErrorMessage() {
+ return error_msg_;
+ }
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(error_code_);
+ out.writeString(error_msg_);
+ }
+}
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 843c146..1cb0d25 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -72,7 +72,7 @@
const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
-const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
+const char* AssetManager::VENDOR_OVERLAY_DIR = "/vendor/overlay";
const char* AssetManager::PRODUCT_OVERLAY_DIR = "/product/overlay";
const char* AssetManager::PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay";
const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme";
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index cdb87bc..e22e2d2 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -59,13 +59,13 @@
public:
static const char* RESOURCES_FILENAME;
static const char* IDMAP_BIN;
- static const char* OVERLAY_DIR;
+ static const char* VENDOR_OVERLAY_DIR;
static const char* PRODUCT_OVERLAY_DIR;
static const char* PRODUCT_SERVICES_OVERLAY_DIR;
/*
* If OVERLAY_THEME_DIR_PROPERTY is set, search for runtime resource overlay
- * APKs in OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to
- * OVERLAY_DIR.
+ * APKs in VENDOR_OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in
+ * addition to VENDOR_OVERLAY_DIR.
*/
static const char* OVERLAY_THEME_DIR_PROPERTY;
static const char* TARGET_PACKAGE_NAME;
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index a1a6d9f..04c4629 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -85,9 +85,6 @@
case StatsLogValue::FLOAT:
mElements.push_back(StatsLogValue(in->readFloat()));
break;
- case StatsLogValue::DOUBLE:
- mElements.push_back(StatsLogValue(in->readDouble()));
- break;
case StatsLogValue::STORAGE:
mElements.push_back(StatsLogValue());
mElements.back().setType(StatsLogValue::STORAGE);
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
index 1cc650b..823af65 100644
--- a/media/java/android/media/AudioPresentation.java
+++ b/media/java/android/media/AudioPresentation.java
@@ -18,7 +18,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.TestApi;
import android.icu.util.ULocale;
import java.lang.annotation.Retention;
@@ -172,6 +171,10 @@
return localeLabels;
}
+ private Map<ULocale, String> getULabels() {
+ return mLabels;
+ }
+
/**
* @return the locale corresponding to audio presentation's ISO 639-1/639-2 language code.
*/
@@ -231,17 +234,24 @@
AudioPresentation obj = (AudioPresentation) o;
return mPresentationId == obj.getPresentationId()
&& mProgramId == obj.getProgramId()
- && mLanguage == obj.getULocale()
+ && mLanguage.equals(obj.getULocale())
&& mMasteringIndication == obj.getMasteringIndication()
&& mAudioDescriptionAvailable == obj.hasAudioDescription()
&& mSpokenSubtitlesAvailable == obj.hasSpokenSubtitles()
&& mDialogueEnhancementAvailable == obj.hasDialogueEnhancement()
- && mLabels.equals(obj.getLabels());
+ && mLabels.equals(obj.getULabels());
}
@Override
public int hashCode() {
- return Objects.hashCode(mPresentationId);
+ return Objects.hash(mPresentationId,
+ mProgramId,
+ mLanguage.hashCode(),
+ mMasteringIndication,
+ mAudioDescriptionAvailable,
+ mSpokenSubtitlesAvailable,
+ mDialogueEnhancementAvailable,
+ mLabels.hashCode());
}
/**
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 24b7f36..cdbc7b44 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -418,9 +418,6 @@
/**
* Returns the status code for the key
- * @return one of {@link #STATUS_USABLE}, {@link #STATUS_EXPIRED},
- * {@link #STATUS_OUTPUT_NOT_ALLOWED}, {@link #STATUS_PENDING}
- * or {@link #STATUS_INTERNAL_ERROR}.
*/
@KeyStatusCode
public int getStatusCode() { return mStatusCode; }
@@ -654,13 +651,7 @@
* can be queried using {@link #getSecurityLevel}. A session
* ID is returned.
*
- * @param level the new security level, one of
- * {@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}.
- *
+ * @param level the new security level
* @throws NotProvisionedException if provisioning is needed
* @throws ResourceBusyException if required resources are in use
* @throws IllegalArgumentException if the requested security level is
@@ -790,9 +781,6 @@
/**
* Get the type of the request
- * @return one of {@link #REQUEST_TYPE_INITIAL},
- * {@link #REQUEST_TYPE_RENEWAL}, {@link #REQUEST_TYPE_RELEASE},
- * {@link #REQUEST_TYPE_NONE} or {@link #REQUEST_TYPE_UPDATE}
*/
@RequestType
public int getRequestType() { return mRequestType; }
@@ -1051,8 +1039,7 @@
* an inactive offline license are not usable for decryption.
*
* @param keySetId selects the offline license
- * @return the offline license state, one of {@link #OFFLINE_LICENSE_USABLE},
- * {@link #OFFLINE_LICENSE_INACTIVE} or {@link #OFFLINE_LICENSE_STATE_UNKNOWN}.
+ * @return the offline license state
* @throws IllegalArgumentException if the keySetId does not refer to an
* offline license.
*/
@@ -1191,9 +1178,7 @@
* enforcing compliance with HDCP requirements. Trusted enforcement of
* HDCP policies must be handled by the DRM system.
* <p>
- * @return one of {@link #HDCP_LEVEL_UNKNOWN}, {@link #HDCP_NONE},
- * {@link #HDCP_V1}, {@link #HDCP_V2}, {@link #HDCP_V2_1}, {@link #HDCP_V2_2}
- * or {@link #HDCP_NO_DIGITAL_OUTPUT}.
+ * @return the connected HDCP level
*/
@HdcpLevel
public native int getConnectedHdcpLevel();
@@ -1204,9 +1189,7 @@
* that may be connected. If multiple HDCP-capable interfaces are present,
* it indicates the highest of the maximum HDCP levels of all interfaces.
* <p>
- * @return one of {@link #HDCP_LEVEL_UNKNOWN}, {@link #HDCP_NONE},
- * {@link #HDCP_V1}, {@link #HDCP_V2}, {@link #HDCP_V2_1}, {@link #HDCP_V2_2}
- * or {@link #HDCP_NO_DIGITAL_OUTPUT}.
+ * @return the maximum supported HDCP level
*/
@HdcpLevel
public native int getMaxHdcpLevel();
@@ -1296,10 +1279,7 @@
* time a session is opened using {@link #openSession}.
* @param sessionId the session to query.
* <p>
- * @return one of {@link #SECURITY_LEVEL_UNKNOWN},
- * {@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}.
+ * @return the security level of the session
*/
@SecurityLevel
public native int getSecurityLevel(@NonNull byte[] sessionId);
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 4e90162..e8b2f65 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -856,11 +856,9 @@
* Checks whether the MediaPlayer2 is looping or non-looping.
*
* @return true if the MediaPlayer2 is currently looping, false otherwise
- * @hide
*/
- public boolean isLooping() {
- return false;
- }
+ // This is a synchronous call.
+ public abstract boolean isLooping();
/**
* Sets the audio session ID.
@@ -875,7 +873,8 @@
* When created, a MediaPlayer2 instance automatically generates its own audio session ID.
* However, it is possible to force this player to be part of an already existing audio session
* by calling this method.
- * This method must be called before one of the overloaded <code> setDataSource </code> methods.
+ * This method must be called when player is in {@link #PLAYER_STATE_IDLE} or
+ * {@link #PLAYER_STATE_PREPARED} state in order to have sessionId take effect.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
@@ -885,8 +884,10 @@
* Returns the audio session ID.
*
* @return the audio session ID. {@see #setAudioSessionId(int)}
- * Note that the audio session ID is 0 only if a problem occured when the MediaPlayer2 was contructed.
+ * Note that the audio session ID is 0 only if a problem occured when the MediaPlayer2 was
+ * contructed.
*/
+ // This is a synchronous call.
public abstract int getAudioSessionId();
/**
@@ -907,7 +908,6 @@
// This is an asynchronous call.
public abstract Object attachAuxEffect(int effectId);
-
/**
* Sets the send level of the player to the attached auxiliary effect.
* See {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0.
@@ -972,36 +972,10 @@
* @return List of track info. The total number of tracks is the array length.
* Must be called again if an external timed text source has been added after
* addTimedTextSource method is called.
+ * @throws IllegalStateException if it is called in an invalid state.
*/
public abstract List<TrackInfo> getTrackInfo();
- /* Do not change these values without updating their counterparts
- * in include/media/stagefright/MediaDefs.h and media/libstagefright/MediaDefs.cpp!
- */
- /**
- * MIME type for SubRip (SRT) container. Used in addTimedTextSource APIs.
- * @hide
- */
- public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
-
- /**
- * MIME type for WebVTT subtitle data.
- * @hide
- */
- public static final String MEDIA_MIMETYPE_TEXT_VTT = "text/vtt";
-
- /**
- * MIME type for CEA-608 closed caption data.
- * @hide
- */
- public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608";
-
- /**
- * MIME type for CEA-708 closed caption data.
- * @hide
- */
- public static final String MEDIA_MIMETYPE_TEXT_CEA_708 = "text/cea-708";
-
/**
* Returns the index of the audio, video, or subtitle track currently selected for playback,
* The return value is an index into the array returned by {@link #getTrackInfo()}, and can
@@ -1202,7 +1176,7 @@
public abstract void unregisterEventCallback(EventCallback eventCallback);
/* Do not change these values without updating their counterparts
- * in include/media/mediaplayer2.h!
+ * in include/media/MediaPlayer2Types.h!
*/
/** Unspecified media player error.
* @see EventCallback#onError
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 9b97b10..babf24e 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -56,7 +56,6 @@
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
@@ -64,7 +63,6 @@
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
-import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
@@ -1015,24 +1013,6 @@
private native final void _seekTo(long msec, int mode);
- /**
- * Get current playback position as a {@link MediaTimestamp}.
- * <p>
- * The MediaTimestamp represents how the media time correlates to the system time in
- * a linear fashion using an anchor and a clock rate. During regular playback, the media
- * time moves fairly constantly (though the anchor frame may be rebased to a current
- * system time, the linear correlation stays steady). Therefore, this method does not
- * need to be called often.
- * <p>
- * To help users get current playback position, this method always anchors the timestamp
- * to the current {@link System#nanoTime system time}, so
- * {@link MediaTimestamp#getAnchorMediaTimeUs} can be used as current playback position.
- *
- * @return a MediaTimestamp object if a timestamp is available, or {@code null} if no timestamp
- * is available, e.g. because the media player has not been initialized.
- *
- * @see MediaTimestamp
- */
@Override
@Nullable
public MediaTimestamp getTimestamp()
@@ -1048,11 +1028,6 @@
}
}
- /**
- * Resets the MediaPlayer2 to its uninitialized state. After calling
- * this method, you will have to initialize it again by setting the
- * data source and calling prepare().
- */
@Override
public void reset() {
synchronized (mEventCbLock) {
@@ -1085,41 +1060,14 @@
// Keep KEY_PARAMETER_* in sync with include/media/mediaplayer2.h
private final static int KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400;
- /**
- * Sets the audio attributes.
- * @param value value of the parameter to be set.
- * @return true if the parameter is set successfully, false otherwise
- */
+
+ // return true if the parameter is set successfully, false otherwise
private native boolean native_setAudioAttributes(AudioAttributes audioAttributes);
private native AudioAttributes native_getAudioAttributes();
-
- /**
- * Checks whether the MediaPlayer2 is looping or non-looping.
- *
- * @return true if the MediaPlayer2 is currently looping, false otherwise
- * @hide
- */
@Override
public native boolean isLooping();
- /**
- * Sets the audio session ID.
- *
- * @param sessionId the audio session ID.
- * The audio session ID is a system wide unique identifier for the audio stream played by
- * this MediaPlayer2 instance.
- * The primary use of the audio session ID is to associate audio effects to a particular
- * instance of MediaPlayer2: if an audio session ID is provided when creating an audio effect,
- * this effect will be applied only to the audio content of media players within the same
- * audio session and not to the output mix.
- * When created, a MediaPlayer2 instance automatically generates its own audio session ID.
- * However, it is possible to force this player to be part of an already existing audio session
- * by calling this method.
- * This method must be called before one of the overloaded <code> setDataSource </code> methods.
- * @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if the sessionId is invalid.
- */
@Override
public Object setAudioSessionId(int sessionId) {
return addTask(new Task(CALL_COMPLETED_SET_AUDIO_SESSION_ID, false) {
@@ -1132,29 +1080,9 @@
private native void _setAudioSessionId(int sessionId);
- /**
- * Returns the audio session ID.
- *
- * @return the audio session ID. {@see #setAudioSessionId(int)}
- * Note that the audio session ID is 0 only if a problem occured when the MediaPlayer2 was contructed.
- */
@Override
public native int getAudioSessionId();
- /**
- * Attaches an auxiliary effect to the player. A typical auxiliary effect is a reverberation
- * effect which can be applied on any sound source that directs a certain amount of its
- * energy to this effect. This amount is defined by setAuxEffectSendLevel().
- * See {@link #setAuxEffectSendLevel(float)}.
- * <p>After creating an auxiliary effect (e.g.
- * {@link android.media.audiofx.EnvironmentalReverb}), retrieve its ID with
- * {@link android.media.audiofx.AudioEffect#getId()} and use it when calling this method
- * to attach the player to the effect.
- * <p>To detach the effect from the player, call this method with a null effect id.
- * <p>This method must be called after one of the overloaded <code> setDataSource </code>
- * methods.
- * @param effectId system wide unique id of the effect to attach
- */
@Override
public Object attachAuxEffect(int effectId) {
return addTask(new Task(CALL_COMPLETED_ATTACH_AUX_EFFECT, false) {
@@ -1167,18 +1095,6 @@
private native void _attachAuxEffect(int effectId);
- /**
- * Sets the send level of the player to the attached auxiliary effect.
- * See {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0.
- * <p>By default the send level is 0, so even if an effect is attached to the player
- * this method must be called for the effect to be applied.
- * <p>Note that the passed level value is a raw scalar. UI controls should be scaled
- * logarithmically: the gain applied by audio framework ranges from -72dB to 0dB,
- * so an appropriate conversion from linear UI input x to level is:
- * x == 0 -> level = 0
- * 0 < x <= R -> level = 10^(72*(x-R)/20/R)
- * @param level send level scalar
- */
@Override
public Object setAuxEffectSendLevel(float level) {
return addTask(new Task(CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL, false) {
@@ -1208,31 +1124,17 @@
* @see android.media.MediaPlayer2#getTrackInfo
*/
public static final class TrackInfoImpl extends TrackInfo {
- /**
- * Gets the track type.
- * @return TrackType which indicates if the track is video, audio, timed text.
- */
@Override
public int getTrackType() {
return mTrackType;
}
- /**
- * Gets the language code of the track.
- * @return a language code in either way of ISO-639-1 or ISO-639-2.
- * When the language is unknown or could not be determined,
- * ISO-639-2 language code, "und", is returned.
- */
@Override
public String getLanguage() {
String language = mFormat.getString(MediaFormat.KEY_LANGUAGE);
return language == null ? "und" : language;
}
- /**
- * Gets the {@link MediaFormat} of the track. If the format is
- * unknown or could not be determined, null is returned.
- */
@Override
public MediaFormat getFormat() {
if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT
@@ -1294,14 +1196,6 @@
}
};
- /**
- * Returns a List of track information.
- *
- * @return List of track info. The total number of tracks is the array length.
- * Must be called again if an external timed text source has been added after
- * addTimedTextSource method is called.
- * @throws IllegalStateException if it is called in an invalid state.
- */
@Override
public List<TrackInfo> getTrackInfo() {
TrackInfoImpl trackInfo[] = getInbandTrackInfoImpl();
@@ -1328,33 +1222,6 @@
return trackInfo;
}
- /*
- * A helper function to check if the mime type is supported by media framework.
- */
- private static boolean availableMimeTypeForExternalSource(String mimeType) {
- if (MEDIA_MIMETYPE_TEXT_SUBRIP.equals(mimeType)) {
- return true;
- }
- return false;
- }
-
- /**
- * Returns the index of the audio, video, or subtitle track currently selected for playback,
- * The return value is an index into the array returned by {@link #getTrackInfo()}, and can
- * be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}.
- *
- * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
- * {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
- * {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
- * @return index of the audio, video, or subtitle track currently selected for playback;
- * a negative integer is returned when there is no selected track for {@code trackType} or
- * when {@code trackType} is not one of audio, video, or subtitle.
- * @throws IllegalStateException if called after {@link #close()}
- *
- * @see #getTrackInfo()
- * @see #selectTrack(int)
- * @see #deselectTrack(int)
- */
@Override
public int getSelectedTrack(int trackType) {
PlayerMessage request = PlayerMessage.newBuilder()
@@ -1368,34 +1235,6 @@
return response.getValues(0).getInt32Value();
}
- /**
- * Selects a track.
- * <p>
- * If a MediaPlayer2 is in invalid state, it throws an IllegalStateException exception.
- * If a MediaPlayer2 is in <em>Started</em> state, the selected track is presented immediately.
- * If a MediaPlayer2 is not in Started state, it just marks the track to be played.
- * </p>
- * <p>
- * In any valid state, if it is called multiple times on the same type of track (ie. Video,
- * Audio, Timed Text), the most recent one will be chosen.
- * </p>
- * <p>
- * The first audio and video tracks are selected by default if available, even though
- * this method is not called. However, no timed text track will be selected until
- * this function is called.
- * </p>
- * <p>
- * Currently, only timed text tracks or audio tracks can be selected via this method.
- * In addition, the support for selecting an audio track at runtime is pretty limited
- * in that an audio track can only be selected in the <em>Prepared</em> state.
- * </p>
- * @param index the index of the track to be selected. The valid range of the index
- * is 0..total number of track - 1. The total number of tracks as well as the type of
- * each individual track can be found by calling {@link #getTrackInfo()} method.
- * @throws IllegalStateException if called in an invalid state.
- *
- * @see android.media.MediaPlayer2#getTrackInfo
- */
@Override
public Object selectTrack(int index) {
return addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) {
@@ -1406,20 +1245,6 @@
});
}
- /**
- * Deselect a track.
- * <p>
- * Currently, the track must be a timed text track and no audio or video tracks can be
- * deselected. If the timed text track identified by index has not been
- * selected before, it throws an exception.
- * </p>
- * @param index the index of the track to be deselected. The valid range of the index
- * is 0..total number of tracks - 1. The total number of tracks as well as the type of
- * each individual track can be found by calling {@link #getTrackInfo()} method.
- * @throws IllegalStateException if called in an invalid state.
- *
- * @see android.media.MediaPlayer2#getTrackInfo
- */
@Override
public Object deselectTrack(int index) {
return addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) {
diff --git a/media/java/android/media/MediaPlayer2Utils.java b/media/java/android/media/MediaPlayer2Utils.java
new file mode 100644
index 0000000..c6dee22
--- /dev/null
+++ b/media/java/android/media/MediaPlayer2Utils.java
@@ -0,0 +1,41 @@
+/*
+ * 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 android.media;
+
+/**
+ * Helper class used by native code to reduce JNI calls from native side.
+ * @hide
+ */
+public class MediaPlayer2Utils {
+ /**
+ * Returns whether audio offloading is supported for the given audio format.
+ *
+ * @param encoding the type of encoding defined in {@link AudioFormat}
+ * @param sampleRate the sampling rate of the stream
+ * @param channelMask the channel mask defined in {@link AudioFormat}
+ */
+ // @CalledByNative
+ public static boolean isOffloadedAudioPlaybackSupported(
+ int encoding, int sampleRate, int channelMask) {
+ final AudioFormat format = new AudioFormat.Builder()
+ .setEncoding(encoding)
+ .setSampleRate(sampleRate)
+ .setChannelMask(channelMask)
+ .build();
+ return AudioManager.isOffloadedPlaybackSupported(format);
+ }
+}
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 0769e5c..c49e720 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -1050,9 +1050,9 @@
android_media_MediaPlayer2_release(env, thiz);
}
-static void android_media_MediaPlayer2_set_audio_session_id(JNIEnv *env, jobject thiz,
+static void android_media_MediaPlayer2_setAudioSessionId(JNIEnv *env, jobject thiz,
jint sessionId) {
- ALOGV("set_session_id(): %d", sessionId);
+ ALOGV("setAudioSessionId(): %d", sessionId);
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
@@ -1062,8 +1062,8 @@
NULL);
}
-static jint android_media_MediaPlayer2_get_audio_session_id(JNIEnv *env, jobject thiz) {
- ALOGV("get_session_id()");
+static jint android_media_MediaPlayer2_getAudioSessionId(JNIEnv *env, jobject thiz) {
+ ALOGV("getAudioSessionId()");
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
@@ -1419,8 +1419,8 @@
{"native_init", "()V", (void *)android_media_MediaPlayer2_native_init},
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer2_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaPlayer2_native_finalize},
- {"getAudioSessionId", "()I", (void *)android_media_MediaPlayer2_get_audio_session_id},
- {"_setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer2_set_audio_session_id},
+ {"getAudioSessionId", "()I", (void *)android_media_MediaPlayer2_getAudioSessionId},
+ {"_setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer2_setAudioSessionId},
{"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer2_setAuxEffectSendLevel},
{"_attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer2_attachAuxEffect},
// Modular DRM
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 4abcf73..c9ee5c8 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -541,14 +541,14 @@
}
@Override
- public Cursor querySearchDocuments(String rootId, String query, String[] projection)
+ public Cursor querySearchDocuments(String rootId, String[] projection, Bundle queryArgs)
throws FileNotFoundException {
final File parent;
synchronized (mRootsLock) {
parent = mRoots.get(rootId).path;
}
- return querySearchDocuments(parent, query, projection, Collections.emptySet());
+ return querySearchDocuments(parent, projection, Collections.emptySet(), queryArgs);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 0dbc037..2f082b9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -152,7 +152,11 @@
private boolean mNetworkScoringUiEnabled;
private long mMaxSpeedLabelScoreCacheAge;
-
+ private static final String WIFI_SECURITY_PSK = "PSK";
+ private static final String WIFI_SECURITY_EAP = "EAP";
+ private static final String WIFI_SECURITY_SAE = "SAE";
+ private static final String WIFI_SECURITY_OWE = "OWE";
+ private static final String WIFI_SECURITY_SUITE_B_192 = "SUITE_B_192";
@VisibleForTesting
Scanner mScanner;
@@ -505,13 +509,18 @@
* {@link #updateAccessPoints(List, List)}.
*/
private void fetchScansAndConfigsAndUpdateAccessPoints() {
- final List<ScanResult> newScanResults = mWifiManager.getScanResults();
+ List<ScanResult> newScanResults = mWifiManager.getScanResults();
+
+ // Filter all unsupported networks from the scan result list
+ final List<ScanResult> filteredScanResults =
+ filterScanResultsByCapabilities(newScanResults);
+
if (isVerboseLoggingEnabled()) {
- Log.i(TAG, "Fetched scan results: " + newScanResults);
+ Log.i(TAG, "Fetched scan results: " + filteredScanResults);
}
List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
- updateAccessPoints(newScanResults, configs);
+ updateAccessPoints(filteredScanResults, configs);
}
/** Update the internal list of access points. */
@@ -937,4 +946,49 @@
mListener.onAccessPointsChanged();
}
+
+ /**
+ * Filters unsupported networks from scan results. New WPA3 networks and OWE networks
+ * may not be compatible with the device HW/SW.
+ * @param scanResults List of scan results
+ * @return List of filtered scan results based on local device capabilities
+ */
+ private List<ScanResult> filterScanResultsByCapabilities(List<ScanResult> scanResults) {
+ if (scanResults == null) {
+ return null;
+ }
+
+ // Get and cache advanced capabilities
+ final boolean isOweSupported = mWifiManager.isOweSupported();
+ final boolean isSaeSupported = mWifiManager.isWpa3SaeSupported();
+ final boolean isSuiteBSupported = mWifiManager.isWpa3SuiteBSupported();
+
+ List<ScanResult> filteredScanResultList = new ArrayList<>();
+
+ // Iterate through the list of scan results and filter out APs which are not
+ // compatible with our device.
+ for (ScanResult scanResult : scanResults) {
+ if (scanResult.capabilities.contains(WIFI_SECURITY_PSK)) {
+ // All devices (today) support RSN-PSK or WPA-PSK
+ // Add this here because some APs may support both PSK and SAE and the check
+ // below will filter it out.
+ filteredScanResultList.add(scanResult);
+ continue;
+ }
+
+ if ((scanResult.capabilities.contains(WIFI_SECURITY_SUITE_B_192) && !isSuiteBSupported)
+ || (scanResult.capabilities.contains(WIFI_SECURITY_SAE) && !isSaeSupported)
+ || (scanResult.capabilities.contains(WIFI_SECURITY_OWE) && !isOweSupported)) {
+ if (isVerboseLoggingEnabled()) {
+ Log.v(TAG, "filterScanResultsByCapabilities: Filtering SSID "
+ + scanResult.SSID + " with capabilities: " + scanResult.capabilities);
+ }
+ } else {
+ // Safe to add
+ filteredScanResultList.add(scanResult);
+ }
+ }
+
+ return filteredScanResultList;
+ }
}
diff --git a/packages/SystemUI/res/drawable/stat_sys_camera.xml b/packages/SystemUI/res/drawable/stat_sys_camera.xml
new file mode 100644
index 0000000..eb3e963
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_camera.xml
@@ -0,0 +1,31 @@
+<?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.
+*/
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="3dp"
+ android:insetRight="3dp">
+ <vector
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFF"
+ android:pathData="M20,5h-3.17L15,3H9L7.17,5H4C2.9,5 2,5.9 2,7v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V7C22,5.9 21.1,5 20,5zM20,19H4V7h16V19zM12,9c-2.21,0 -4,1.79 -4,4c0,2.21 1.79,4 4,4s4,-1.79 4,-4C16,10.79 14.21,9 12,9z"/>
+ </vector>
+</inset>
diff --git a/packages/SystemUI/res/drawable/stat_sys_mic_none.xml b/packages/SystemUI/res/drawable/stat_sys_mic_none.xml
new file mode 100644
index 0000000..d6bdf9f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_mic_none.xml
@@ -0,0 +1,30 @@
+<?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.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="18dp"
+ android:height="18dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFF"
+ android:pathData="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6C9,12.66 10.34,14 12,14zM11,5c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v6c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V5z"/>
+ <path
+ android:fillColor="#FFF"
+ android:pathData="M17,11c0,2.76 -2.24,5 -5,5s-5,-2.24 -5,-5H5c0,3.53 2.61,6.43 6,6.92V21h2v-3.08c3.39,-0.49 6,-3.39 6,-6.92H17z"/>
+</vector>
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index f409902..3d9aa0f 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -23,9 +23,9 @@
typealias Privacy = PrivacyType
enum class PrivacyType(val nameId: Int, val iconId: Int) {
- TYPE_CAMERA(R.string.privacy_type_camera, com.android.internal.R.drawable.ic_camera),
+ TYPE_CAMERA(R.string.privacy_type_camera, R.drawable.stat_sys_camera),
TYPE_LOCATION(R.string.privacy_type_location, R.drawable.stat_sys_location),
- TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.ic_mic_26dp);
+ TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.stat_sys_mic_none);
fun getName(context: Context) = context.resources.getString(nameId)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index b838c9b..37bf06e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -21,6 +21,8 @@
import static android.service.notification.NotificationListenerService.Ranking
.USER_SENTIMENT_NEGATIVE;
+import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;
+
import android.app.INotificationManager;
import android.app.NotificationChannel;
import android.content.Context;
@@ -189,7 +191,13 @@
} else if (gutsView instanceof AppOpsInfo) {
initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
} else if (gutsView instanceof NotificationInfo) {
- initializeNotificationInfo(row, (NotificationInfo) gutsView);
+ int action;
+ if (item instanceof NotificationMenuRow.NotificationInfoMenuItem) {
+ action = ((NotificationMenuRow.NotificationInfoMenuItem) item).mAction;
+ } else {
+ action = ACTION_NONE;
+ }
+ initializeNotificationInfo(row, (NotificationInfo) gutsView, action);
}
return true;
} catch (Exception e) {
@@ -246,14 +254,15 @@
/**
* Sets up the {@link NotificationInfo} inside the notification row's guts.
- *
* @param row view to set up the guts for
* @param notificationInfoView view to set up/bind within {@code row}
+ * @param action The action to take immediately upon binding, if any.
*/
@VisibleForTesting
void initializeNotificationInfo(
final ExpandableNotificationRow row,
- NotificationInfo notificationInfoView) throws Exception {
+ NotificationInfo notificationInfoView,
+ @NotificationInfo.NotificationInfoAction int action) throws Exception {
NotificationGuts guts = row.getGuts();
StatusBarNotification sbn = row.getStatusBarNotification();
String packageName = sbn.getPackageName();
@@ -297,7 +306,8 @@
isForBlockingHelper,
row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE,
row.getEntry().noisy,
- row.getEntry().importance);
+ row.getEntry().importance,
+ action);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 522da4d..3a7091b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -71,16 +71,19 @@
public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
private static final String TAG = "InfoGuts";
- @IntDef(prefix = { "SWAP_CONTENT_" }, value = {
- SWAP_CONTENT_UNDO,
- SWAP_CONTENT_TOGGLE_SILENT,
- SWAP_CONTENT_BLOCK,
+ @IntDef(prefix = { "ACTION_" }, value = {
+ ACTION_NONE,
+ ACTION_UNDO,
+ ACTION_TOGGLE_SILENT,
+ ACTION_BLOCK,
})
- @interface SwapContentAction {}
+ public @interface NotificationInfoAction {
+ }
- private static final int SWAP_CONTENT_UNDO = 0;
- private static final int SWAP_CONTENT_TOGGLE_SILENT = 1;
- private static final int SWAP_CONTENT_BLOCK = 2;
+ public static final int ACTION_NONE = 0;
+ public static final int ACTION_UNDO = 1;
+ public static final int ACTION_TOGGLE_SILENT = 2;
+ public static final int ACTION_BLOCK = 3;
private INotificationManager mINotificationManager;
private PackageManager mPm;
@@ -123,8 +126,7 @@
private OnClickListener mOnToggleSilent = v -> {
Runnable saveImportance = () -> {
- mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT;
- swapContent(SWAP_CONTENT_TOGGLE_SILENT);
+ swapContent(ACTION_TOGGLE_SILENT, true /* animate */);
};
if (mCheckSaveListener != null) {
mCheckSaveListener.checkSave(saveImportance, mSbn);
@@ -135,8 +137,7 @@
private OnClickListener mOnStopOrMinimizeNotifications = v -> {
Runnable saveImportance = () -> {
- mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS;
- swapContent(SWAP_CONTENT_BLOCK);
+ swapContent(ACTION_BLOCK, true /* animate */);
};
if (mCheckSaveListener != null) {
mCheckSaveListener.checkSave(saveImportance, mSbn);
@@ -149,7 +150,7 @@
// Reset exit counter that we'll log and record an undo event separately (not an exit event)
mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
- swapContent(SWAP_CONTENT_UNDO);
+ swapContent(ACTION_UNDO, true /* animate */);
};
public NotificationInfo(Context context, AttributeSet attrs) {
@@ -185,13 +186,14 @@
boolean isDeviceProvisioned,
boolean isNonblockable,
boolean isNoisy,
- int importance)
+ int importance,
+ @NotificationInfoAction int action)
throws RemoteException {
bindNotification(pm, iNotificationManager, pkg, notificationChannel,
numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
onAppSettingsClick, isDeviceProvisioned, isNonblockable,
false /* isBlockingHelper */, false /* isUserSentimentNegative */, isNoisy,
- importance);
+ importance, action);
}
public void bindNotification(
@@ -209,7 +211,8 @@
boolean isForBlockingHelper,
boolean isUserSentimentNegative,
boolean isNoisy,
- int importance)
+ int importance,
+ @NotificationInfoAction int action)
throws RemoteException {
mINotificationManager = iNotificationManager;
mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -250,6 +253,10 @@
bindHeader();
bindPrompt();
bindButtons();
+
+ if (action != ACTION_NONE) {
+ swapContent(action, false /* don't animate */);
+ }
}
private void bindHeader() throws RemoteException {
@@ -351,7 +358,8 @@
}
private void saveImportance() {
- if (!mIsNonblockable) {
+ if (!mIsNonblockable
+ || mExitReason != NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS) {
updateImportance();
}
}
@@ -421,7 +429,7 @@
}
}
- private void swapContent(@SwapContentAction int action) {
+ private void swapContent(@NotificationInfoAction int action, boolean animate) {
if (mExpandAnimation != null) {
mExpandAnimation.cancel();
}
@@ -432,10 +440,11 @@
View header = findViewById(R.id.header);
switch (action) {
- case SWAP_CONTENT_UNDO:
+ case ACTION_UNDO:
mChosenImportance = mStartingChannelImportance;
break;
- case SWAP_CONTENT_TOGGLE_SILENT:
+ case ACTION_TOGGLE_SILENT:
+ mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT;
if (mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT) {
mChosenImportance = IMPORTANCE_LOW;
confirmationText.setText(R.string.notification_channel_silenced);
@@ -444,7 +453,8 @@
confirmationText.setText(R.string.notification_channel_unsilenced);
}
break;
- case SWAP_CONTENT_BLOCK:
+ case ACTION_BLOCK:
+ mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS;
if (mIsForeground) {
mChosenImportance = IMPORTANCE_MIN;
confirmationText.setText(R.string.notification_channel_minimized);
@@ -457,38 +467,41 @@
throw new IllegalArgumentException();
}
- boolean isUndo = action == SWAP_CONTENT_UNDO;
- ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA,
- prompt.getAlpha(), isUndo ? 1f : 0f);
- promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
- ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
- confirmation.getAlpha(), isUndo ? 0f : 1f);
- confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
+ boolean isUndo = action == ACTION_UNDO;
prompt.setVisibility(isUndo ? VISIBLE : GONE);
confirmation.setVisibility(isUndo ? GONE : VISIBLE);
header.setVisibility(isUndo ? VISIBLE : GONE);
- mExpandAnimation = new AnimatorSet();
- mExpandAnimation.playTogether(promptAnim, confirmAnim);
- mExpandAnimation.setDuration(150);
- mExpandAnimation.addListener(new AnimatorListenerAdapter() {
- boolean cancelled = false;
+ if (animate) {
+ ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA,
+ prompt.getAlpha(), isUndo ? 1f : 0f);
+ promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
+ ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
+ confirmation.getAlpha(), isUndo ? 0f : 1f);
+ confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
- @Override
- public void onAnimationCancel(Animator animation) {
- cancelled = true;
- }
+ mExpandAnimation = new AnimatorSet();
+ mExpandAnimation.playTogether(promptAnim, confirmAnim);
+ mExpandAnimation.setDuration(150);
+ mExpandAnimation.addListener(new AnimatorListenerAdapter() {
+ boolean mCancelled = false;
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!cancelled) {
- prompt.setVisibility(isUndo ? VISIBLE : GONE);
- confirmation.setVisibility(isUndo ? GONE : VISIBLE);
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
}
- }
- });
- mExpandAnimation.start();
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCancelled) {
+ prompt.setVisibility(isUndo ? VISIBLE : GONE);
+ confirmation.setVisibility(isUndo ? GONE : VISIBLE);
+ }
+ }
+ });
+ mExpandAnimation.start();
+ }
// Since we're swapping/update the content, reset the timeout so the UI can't close
// immediately after the update.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 674c8ee..2bc4f02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
+import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -41,6 +42,7 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
+import com.android.systemui.statusbar.notification.row.NotificationInfo.NotificationInfoAction;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
@@ -610,8 +612,8 @@
String infoDescription = res.getString(R.string.notification_menu_gear_description);
NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
R.layout.notification_info, null, false);
- MenuItem info = new NotificationMenuItem(context, infoDescription, infoContent,
- R.drawable.ic_settings);
+ MenuItem info = new NotificationInfoMenuItem(context, infoDescription, infoContent,
+ R.drawable.ic_settings, ACTION_NONE);
return info;
}
@@ -737,4 +739,18 @@
return mContentDescription;
}
}
+
+ /** A {@link NotificationMenuItem} with an associated {@link NotificationInfoAction}. */
+ public static class NotificationInfoMenuItem extends NotificationMenuItem {
+
+ @NotificationInfoAction
+ int mAction;
+
+ public NotificationInfoMenuItem(Context context, String s,
+ NotificationInfo content, int iconResId,
+ @NotificationInfoAction int action) {
+ super(context, s, content, iconResId);
+ this.mAction = action;
+ }
+ }
}
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 0e6efc8..c84f3db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -66,6 +66,8 @@
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.UiOffloadThread;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -101,7 +103,8 @@
*/
public class PhoneStatusBarPolicy implements Callback, Callbacks,
RotationLockControllerCallback, Listener, LocationChangeCallback,
- ZenModeController.Callback, DeviceProvisionedListener, KeyguardMonitor.Callback {
+ ZenModeController.Callback, DeviceProvisionedListener, KeyguardMonitor.Callback,
+ PrivacyItemController.Callback {
private static final String TAG = "PhoneStatusBarPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -120,6 +123,8 @@
private final String mSlotHeadset;
private final String mSlotDataSaver;
private final String mSlotLocation;
+ private final String mSlotMicrophone;
+ private final String mSlotCamera;
private final Context mContext;
private final Handler mHandler = new Handler();
@@ -136,6 +141,7 @@
private final DeviceProvisionedController mProvisionedController;
private final KeyguardMonitor mKeyguardMonitor;
private final LocationController mLocationController;
+ private final PrivacyItemController mPrivacyItemController;
private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>();
private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
@@ -169,6 +175,7 @@
mProvisionedController = Dependency.get(DeviceProvisionedController.class);
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
mLocationController = Dependency.get(LocationController.class);
+ mPrivacyItemController = new PrivacyItemController(mContext, this);
mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
@@ -183,6 +190,8 @@
mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
+ mSlotMicrophone = context.getString(com.android.internal.R.string.status_bar_microphone);
+ mSlotCamera = context.getString(com.android.internal.R.string.status_bar_camera);
// listen for broadcasts
IntentFilter filter = new IntentFilter();
@@ -241,6 +250,12 @@
context.getString(R.string.accessibility_data_saver_on));
mIconController.setIconVisibility(mSlotDataSaver, false);
+ // privacy items
+ mIconController.setIcon(mSlotMicrophone, R.drawable.stat_sys_mic_none, null);
+ mIconController.setIconVisibility(mSlotMicrophone, false);
+ mIconController.setIcon(mSlotCamera, R.drawable.stat_sys_camera, null);
+ mIconController.setIconVisibility(mSlotCamera, false);
+
mRotationLockController.addCallback(this);
mBluetooth.addCallback(this);
mProvisionedController.addCallback(this);
@@ -251,6 +266,7 @@
mDataSaver.addCallback(this);
mKeyguardMonitor.addCallback(this);
mLocationController.addCallback(this);
+ mPrivacyItemController.setListening(true);
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
@@ -279,6 +295,7 @@
mDataSaver.removeCallback(this);
mKeyguardMonitor.removeCallback(this);
mLocationController.removeCallback(this);
+ mPrivacyItemController.setListening(false);
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
mContext.unregisterReceiver(mIntentReceiver);
@@ -798,6 +815,34 @@
mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
}
+ @Override // PrivacyItemController.Callback
+ public void privacyChanged(List<PrivacyItem> privacyItems) {
+ updatePrivacyItems(privacyItems);
+ }
+
+ private void updatePrivacyItems(List<PrivacyItem> items) {
+ boolean showCamera = false;
+ boolean showMicrophone = false;
+ boolean showLocation = false;
+ for (PrivacyItem item : items) {
+ switch (item.getPrivacyType()) {
+ case TYPE_CAMERA:
+ showCamera = true;
+ break;
+ case TYPE_LOCATION:
+ showLocation = true;
+ break;
+ case TYPE_MICROPHONE:
+ showMicrophone = true;
+ break;
+ }
+ }
+
+ mIconController.setIconVisibility(mSlotCamera, showCamera);
+ mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
+ mIconController.setIconVisibility(mSlotLocation, showLocation);
+ }
+
private final TaskStackChangeListener mTaskListener = new TaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
index d85e18c..67da8a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
@@ -14,14 +14,11 @@
package com.android.systemui.statusbar.policy;
-import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
-import java.util.List;
-
/**
* For mocking because AccessibilityManager is final for some reason...
*/
@@ -62,8 +59,8 @@
mAccessibilityManager.sendAccessibilityEvent(event);
}
- public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
- int feedbackTypeFlags) {
- return mAccessibilityManager.getEnabledAccessibilityServiceList(feedbackTypeFlags);
+ /** Returns a recommended ui timeout value in milliseconds. */
+ public int getRecommendedTimeoutMillis(int originalTimeout, int uiContentFlags) {
+ return mAccessibilityManager.getRecommendedTimeoutMillis(originalTimeout, uiContentFlags);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 798f8bc..b588305 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -16,8 +16,6 @@
package com.android.systemui.volume;
-import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
-import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
@@ -32,7 +30,6 @@
import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
-import android.accessibilityservice.AccessibilityServiceInfo;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -68,13 +65,12 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.View.AccessibilityDelegate;
-import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
+import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
@@ -113,6 +109,10 @@
private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
private static final int UPDATE_ANIMATION_DURATION = 80;
+ static final int DIALOG_TIMEOUT_MILLIS = 3000;
+ static final int DIALOG_SAFETYWARNING_TIMEOUT_MILLIS = 5000;
+ static final int DIALOG_HOVERING_TIMEOUT_MILLIS = 16000;
+
private final Context mContext;
private final H mHandler = new H();
private final VolumeDialogController mController;
@@ -170,7 +170,6 @@
@Override
public void destroy() {
- mAccessibility.destroy();
mController.removeCallback(mControllerCallbackH);
mHandler.removeCallbacksAndMessages(null);
}
@@ -356,8 +355,6 @@
writer.print(" mDynamic: "); writer.println(mDynamic);
writer.print(" mAutomute: "); writer.println(mAutomute);
writer.print(" mSilentMode: "); writer.println(mSilentMode);
- writer.print(" mAccessibility.mFeedbackEnabled: ");
- writer.println(mAccessibility.mFeedbackEnabled);
}
private static int getImpliedLevel(SeekBar seekBar, int progress) {
@@ -571,10 +568,18 @@
}
private int computeTimeoutH() {
- if (mAccessibility.mFeedbackEnabled) return 20000;
- if (mHovering) return 16000;
- if (mSafetyWarning != null) return 5000;
- return 3000;
+ if (mHovering) {
+ return mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_HOVERING_TIMEOUT_MILLIS,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ }
+ if (mSafetyWarning != null) {
+ return mAccessibilityMgr.getRecommendedTimeoutMillis(
+ DIALOG_SAFETYWARNING_TIMEOUT_MILLIS,
+ AccessibilityManager.FLAG_CONTENT_TEXT
+ | AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ }
+ return mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS);
}
protected void dismissH(int reason) {
@@ -1261,28 +1266,8 @@
}
private final class Accessibility extends AccessibilityDelegate {
- private boolean mFeedbackEnabled;
-
public void init() {
- mDialogView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
- @Override
- public void onViewDetachedFromWindow(View v) {
- if (D.BUG) Log.d(TAG, "onViewDetachedFromWindow");
- }
-
- @Override
- public void onViewAttachedToWindow(View v) {
- if (D.BUG) Log.d(TAG, "onViewAttachedToWindow");
- updateFeedbackEnabled();
- }
- });
mDialogView.setAccessibilityDelegate(this);
- mAccessibilityMgr.addCallback(mListener);
- updateFeedbackEnabled();
- }
-
- public void destroy() {
- mAccessibilityMgr.removeCallback(mListener);
}
@Override
@@ -1298,25 +1283,6 @@
rescheduleTimeoutH();
return super.onRequestSendAccessibilityEvent(host, child, event);
}
-
- private void updateFeedbackEnabled() {
- mFeedbackEnabled = computeFeedbackEnabled();
- }
-
- private boolean computeFeedbackEnabled() {
- // are there any enabled non-generic a11y services?
- final List<AccessibilityServiceInfo> services =
- mAccessibilityMgr.getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK);
- for (AccessibilityServiceInfo asi : services) {
- if (asi.feedbackType != 0 && asi.feedbackType != FEEDBACK_GENERIC) {
- return true;
- }
- }
- return false;
- }
-
- private final AccessibilityServicesStateChangeListener mListener =
- enabled -> updateFeedbackEnabled();
}
private static class VolumeRow {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 626726d..3d2ea70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -284,7 +284,8 @@
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView,
+ NotificationInfo.ACTION_NONE);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -301,7 +302,8 @@
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
eq(false) /*isNoisy */,
- eq(0));
+ eq(0),
+ eq(NotificationInfo.ACTION_NONE));
}
@Test
@@ -313,7 +315,8 @@
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView,
+ NotificationInfo.ACTION_NONE);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -330,7 +333,8 @@
eq(false) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
eq(false) /*isNoisy */,
- eq(0));
+ eq(0),
+ eq(NotificationInfo.ACTION_NONE));
}
@Test
@@ -343,7 +347,8 @@
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView,
+ NotificationInfo.ACTION_NONE);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -360,7 +365,8 @@
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
eq(true) /*isNoisy */,
- eq(0));
+ eq(0),
+ eq(NotificationInfo.ACTION_NONE));
}
@Test
@@ -373,7 +379,8 @@
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView,
+ NotificationInfo.ACTION_NONE);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -390,7 +397,8 @@
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
eq(false) /*isNoisy */,
- eq(IMPORTANCE_DEFAULT));
+ eq(IMPORTANCE_DEFAULT),
+ eq(NotificationInfo.ACTION_NONE));
}
@Test
@@ -403,7 +411,8 @@
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
- mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView,
+ NotificationInfo.ACTION_NONE);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -420,7 +429,39 @@
eq(false) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
eq(false) /*isNoisy */,
- eq(0));
+ eq(0),
+ eq(NotificationInfo.ACTION_NONE));
+ }
+
+ @Test
+ public void testInitializeNotificationInfoView_withInitialAction() throws Exception {
+ NotificationInfo notificationInfoView = mock(NotificationInfo.class);
+ ExpandableNotificationRow row = spy(mHelper.createRow());
+ row.setBlockingHelperShowing(true);
+ row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+ when(row.getIsNonblockable()).thenReturn(false);
+ StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView,
+ NotificationInfo.ACTION_BLOCK);
+
+ verify(notificationInfoView).bindNotification(
+ any(PackageManager.class),
+ any(INotificationManager.class),
+ eq(statusBarNotification.getPackageName()),
+ any(NotificationChannel.class),
+ anyInt(),
+ eq(statusBarNotification),
+ any(NotificationInfo.CheckSaveListener.class),
+ any(NotificationInfo.OnSettingsClickListener.class),
+ any(NotificationInfo.OnAppSettingsClickListener.class),
+ eq(false),
+ eq(false),
+ eq(true) /* isForBlockingHelper */,
+ eq(true) /* isUserSentimentNegative */,
+ eq(false) /*isNoisy */,
+ eq(0),
+ eq(NotificationInfo.ACTION_BLOCK));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 3744196..1cc1c63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -187,7 +187,7 @@
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -200,7 +200,7 @@
.thenReturn(iconDrawable);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@@ -209,7 +209,7 @@
public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(GONE, groupNameView.getVisibility());
final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
@@ -226,7 +226,7 @@
.thenReturn(notificationChannelGroup);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
assertEquals("Test Group Name", groupNameView.getText());
@@ -238,7 +238,7 @@
public void testBindNotification_SetsTextChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
}
@@ -247,7 +247,7 @@
public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
- false, false, IMPORTANCE_DEFAULT);
+ false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, textView.getVisibility());
}
@@ -260,7 +260,7 @@
eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
- false, false, IMPORTANCE_DEFAULT);
+ false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -269,7 +269,7 @@
public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -278,7 +278,7 @@
public void testBindNotification_BlockButton() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final View block = mNotificationInfo.findViewById(R.id.block);
final View toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
final View minimize = mNotificationInfo.findViewById(R.id.minimize);
@@ -292,7 +292,7 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT);
+ true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
assertEquals(VISIBLE, toggleSilent.getVisibility());
assertEquals(
@@ -304,7 +304,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_LOW);
+ true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
assertEquals(VISIBLE, toggleSilent.getVisibility());
assertEquals(
@@ -316,7 +316,7 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT);
+ true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
assertEquals(VISIBLE, toggleSilent.getVisibility());
assertEquals(
@@ -329,7 +329,7 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_LOW);
+ true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
assertEquals(VISIBLE, toggleSilent.getVisibility());
assertEquals(
@@ -341,7 +341,7 @@
mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final View block = mNotificationInfo.findViewById(R.id.block);
final View minimize = mNotificationInfo.findViewById(R.id.minimize);
assertEquals(GONE, block.getVisibility());
@@ -356,7 +356,7 @@
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
latch.countDown();
- }, null, true, false, false, IMPORTANCE_DEFAULT);
+ }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -368,7 +368,7 @@
public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -380,7 +380,7 @@
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
- }, null, false, false, false, IMPORTANCE_DEFAULT);
+ }, null, false, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -389,11 +389,11 @@
public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
- }, null, true, false, false, IMPORTANCE_DEFAULT);
+ }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
}
@@ -402,7 +402,7 @@
public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
}
@@ -411,7 +411,7 @@
public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
- true, true, false, IMPORTANCE_DEFAULT);
+ true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger, times(1)).count(anyString(), anyInt());
}
@@ -424,7 +424,7 @@
(View v, NotificationChannel c, int appUid) -> {
assertEquals(null, c);
latch.countDown();
- }, null, true, true, false, IMPORTANCE_DEFAULT);
+ }, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.info).performClick();
// Verify that listener was triggered.
@@ -437,7 +437,7 @@
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
- null, true, true, false, IMPORTANCE_DEFAULT);
+ null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView channelNameView =
mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, channelNameView.getVisibility());
@@ -448,7 +448,7 @@
public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
- null, true, true, false, IMPORTANCE_DEFAULT);
+ null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView blockView = mNotificationInfo.findViewById(R.id.block);
assertEquals(GONE, blockView.getVisibility());
}
@@ -457,7 +457,7 @@
public void testbindNotification_BlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false,
- true, true, false, IMPORTANCE_DEFAULT);
+ true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
@@ -467,7 +467,7 @@
public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -478,7 +478,7 @@
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -489,7 +489,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
mTestableLooper.processAllMessages();
@@ -503,7 +503,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.minimize).performClick();
mTestableLooper.processAllMessages();
@@ -517,7 +517,7 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT);
+ true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
mTestableLooper.processAllMessages();
@@ -531,7 +531,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT);
+ true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
mTestableLooper.processAllMessages();
@@ -545,7 +545,7 @@
int originalImportance = mNotificationChannel.getImportance();
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -560,7 +560,7 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.handleCloseControls(true, false);
@@ -578,7 +578,8 @@
TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
- true, false /* isNonblockable */, false, /* isNoisy */IMPORTANCE_DEFAULT);
+ true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT,
+ NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -598,8 +599,9 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
- null /* onSettingsClick */, null /* onAppSettingsClick */ ,
- true, false /* isNonblockable */, false, /* isNoisy */IMPORTANCE_DEFAULT);
+ null /* onSettingsClick */, null /* onAppSettingsClick */,
+ true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT,
+ NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -620,7 +622,8 @@
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT);
+ true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
+ NotificationInfo.ACTION_NONE);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -648,7 +651,8 @@
10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT);
+ true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
+ NotificationInfo.ACTION_NONE);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -676,8 +680,8 @@
10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true, true /* isUserSentimentNegative */, false, /* isNoisy */
- IMPORTANCE_DEFAULT);
+ true, true /* isUserSentimentNegative */, false /* isNoisy */,
+ IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
@@ -696,7 +700,8 @@
null /* onSettingsClick */, null /* onAppSettingsClick */,
true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT);
+ true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
+ NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
mTestableLooper.processAllMessages();
@@ -719,7 +724,7 @@
true /* isForBlockingHelper */,
true,
false /* isUserSentimentNegative */,
- false, /* isNoisy */IMPORTANCE_DEFAULT);
+ false /* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
NotificationGuts guts = mock(NotificationGuts.class);
doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
mNotificationInfo.setGutsParent(guts);
@@ -734,7 +739,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -748,7 +753,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -781,7 +786,7 @@
false /* isNonblockable */,
true /* isForBlockingHelper */,
true /* isUserSentimentNegative */,
- false, /* isNoisy */IMPORTANCE_DEFAULT);
+ false/* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -803,7 +808,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -818,7 +823,7 @@
mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -839,7 +844,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.handleCloseControls(true, false);
@@ -857,7 +862,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -879,7 +884,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -901,7 +906,7 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT);
+ true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
waitForUndoButton();
@@ -922,7 +927,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
waitForUndoButton();
@@ -944,7 +949,7 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT);
+ true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
waitForUndoButton();
@@ -966,7 +971,7 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_LOW);
+ false, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
waitForUndoButton();
@@ -987,7 +992,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1003,7 +1008,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -1020,7 +1025,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
- }, null, null, true, true, false, IMPORTANCE_DEFAULT);
+ }, null, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
mTestableLooper.processAllMessages();
@@ -1038,7 +1043,8 @@
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
- }, null, null, true, false, false, IMPORTANCE_DEFAULT);
+ }, null, null, true, false, false, IMPORTANCE_DEFAULT,
+ NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
mTestableLooper.processAllMessages();
@@ -1074,7 +1080,7 @@
TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null,
(View v, Intent intent) -> {
latch.countDown();
- }, true, false, false, IMPORTANCE_DEFAULT);
+ }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
assertEquals(View.VISIBLE, settingsLink.getVisibility());
settingsLink.performClick();
@@ -1102,7 +1108,7 @@
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
(View v, Intent intent) -> {
latch.countDown();
- }, true, false, false, IMPORTANCE_DEFAULT);
+ }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
assertEquals(View.VISIBLE, settingsLink.getVisibility());
settingsLink.performClick();
@@ -1121,7 +1127,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
- null, true, false, false, IMPORTANCE_DEFAULT);
+ null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
assertEquals(GONE, settingsLink.getVisibility());
}
@@ -1142,7 +1148,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
assertEquals(GONE, settingsLink.getVisibility());
}
@@ -1165,7 +1171,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true,
- true, true, false, IMPORTANCE_DEFAULT);
+ true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
assertEquals(GONE, settingsLink.getVisibility());
}
@@ -1182,7 +1188,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1195,7 +1201,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -1208,7 +1214,7 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- true, IMPORTANCE_DEFAULT);
+ true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
waitForUndoButton();
@@ -1222,7 +1228,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- true, IMPORTANCE_DEFAULT);
+ true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
waitForUndoButton();
@@ -1236,7 +1242,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -1248,7 +1254,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT);
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -1256,4 +1262,60 @@
waitForStopButton();
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
}
+
+ @Test
+ public void testBindNotificationWithInitialBlockAction() throws Exception {
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_BLOCK);
+ waitForUndoButton();
+ mNotificationInfo.handleCloseControls(true, false);
+
+ mTestableLooper.processAllMessages();
+ ArgumentCaptor<NotificationChannel> updated =
+ ArgumentCaptor.forClass(NotificationChannel.class);
+ verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+ anyString(), eq(TEST_UID), updated.capture());
+ assertTrue((updated.getValue().getUserLockedFields()
+ & USER_LOCKED_IMPORTANCE) != 0);
+ assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance());
+ }
+
+ @Test
+ public void testBindNotificationWithInitialSilenceAction() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_TOGGLE_SILENT);
+ waitForUndoButton();
+ mNotificationInfo.handleCloseControls(true, false);
+
+ mTestableLooper.processAllMessages();
+ ArgumentCaptor<NotificationChannel> updated =
+ ArgumentCaptor.forClass(NotificationChannel.class);
+ verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+ anyString(), eq(TEST_UID), updated.capture());
+ assertTrue((updated.getValue().getUserLockedFields()
+ & USER_LOCKED_IMPORTANCE) != 0);
+ assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance());
+ }
+
+ @Test
+ public void testBindNotificationWithInitialUnSilenceAction() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ true, IMPORTANCE_LOW, NotificationInfo.ACTION_TOGGLE_SILENT);
+ waitForUndoButton();
+ mNotificationInfo.handleCloseControls(true, false);
+
+ mTestableLooper.processAllMessages();
+ ArgumentCaptor<NotificationChannel> updated =
+ ArgumentCaptor.forClass(NotificationChannel.class);
+ verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+ anyString(), eq(TEST_UID), updated.capture());
+ assertTrue((updated.getValue().getUserLockedFields()
+ & USER_LOCKED_IMPORTANCE) != 0);
+ assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index c536dca..5f02fad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -27,18 +27,23 @@
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.KeyguardManager;
import android.media.AudioManager;
+import android.os.SystemClock;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.text.TextUtils;
+import android.view.InputDevice;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
import android.widget.ImageView;
import com.android.systemui.R;
@@ -48,10 +53,11 @@
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.function.Predicate;
@@ -59,7 +65,6 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-@Ignore
public class VolumeDialogImplTest extends SysuiTestCase {
VolumeDialogImpl mDialog;
@@ -113,6 +118,45 @@
+ " failed test", condition.test(view));
}
}
+
+ @Test
+ public void testComputeTimeout() {
+ Mockito.reset(mAccessibilityMgr);
+ mDialog.rescheduleTimeoutH();
+ verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
+ VolumeDialogImpl.DIALOG_TIMEOUT_MILLIS,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ }
+
+ @Test
+ public void testComputeTimeout_withHovering() {
+ Mockito.reset(mAccessibilityMgr);
+ View dialog = mDialog.getDialogView();
+ long uptimeMillis = SystemClock.uptimeMillis();
+ MotionEvent event = MotionEvent.obtain(uptimeMillis, uptimeMillis,
+ MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
+ event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ dialog.dispatchGenericMotionEvent(event);
+ event.recycle();
+ verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
+ VolumeDialogImpl.DIALOG_HOVERING_TIMEOUT_MILLIS,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ }
+
+ @Test
+ public void testComputeTimeout_withSafetyWarningOn() {
+ Mockito.reset(mAccessibilityMgr);
+ ArgumentCaptor<VolumeDialogController.Callbacks> controllerCallbackCapture =
+ ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class);
+ verify(mController).addCallback(controllerCallbackCapture.capture(), any());
+ VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue();
+ callbacks.onShowSafetyWarning(AudioManager.FLAG_SHOW_UI);
+ verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
+ VolumeDialogImpl.DIALOG_SAFETYWARNING_TIMEOUT_MILLIS,
+ AccessibilityManager.FLAG_CONTENT_TEXT
+ | AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ }
+
/*
@Test
public void testContentDescriptions() {
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 8ae5872..3e07d12 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -844,6 +844,8 @@
// PACKAGE: App that posted the notification
// DETAIL: Notification is expanded by user.
// PACKAGE: App that posted the notification
+ // COLLAPSE: Notification is collapsed by user.
+ // PACKAGE: App that posted the notification
// DISMISS: Notification is dismissed.
// PACKAGE: App that posted the notification
// SUBTYPE: Dismiss reason from NotificationManagerService.java
@@ -6596,6 +6598,12 @@
// OS: Q
NOTIFICATION_ZEN_MODE_OVERRIDING_APP = 1589;
+ // ACTION: User sent a direct reply
+ // PACKAGE: App that posted the notification
+ // CATEGORY: NOTIFICATION
+ // OS: Q
+ NOTIFICATION_DIRECT_REPLY_ACTION = 1590;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 4205ac7..31238df 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -166,12 +166,12 @@
context.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
}
- @Override // from MasterSystemService
+ @Override // from AbstractMasterSystemService
protected String getServiceSettingsProperty() {
return Settings.Secure.AUTOFILL_SERVICE;
}
- @Override // from MasterSystemService
+ @Override // from AbstractMasterSystemService
protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
@NonNull ContentObserver observer) {
resolver.registerContentObserver(Settings.Global.getUriFor(
@@ -188,7 +188,7 @@
UserHandle.USER_ALL);
}
- @Override // from MasterSystemService
+ @Override // from AbstractMasterSystemService
protected void onSettingsChanged(int userId, @NonNull String property) {
switch (property) {
case Settings.Global.AUTOFILL_LOGGING_LEVEL:
@@ -210,25 +210,24 @@
}
}
- @Override // from MasterSystemService
- protected AutofillManagerServiceImpl newServiceLocked(int resolvedUserId, boolean disabled) {
+ @Override // from AbstractMasterSystemService
+ protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
+ boolean disabled) {
return new AutofillManagerServiceImpl(this, mLock, mRequestsHistory,
mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi, mAutofillCompatState,
disabled);
}
- @Override // MasterSystemService
- protected AutofillManagerServiceImpl removeCachedServiceLocked(int userId) {
- final AutofillManagerServiceImpl service = super.removeCachedServiceLocked(userId);
- if (service != null) {
- service.destroyLocked();
- mAutofillCompatState.removeCompatibilityModeRequests(userId);
- }
- return service;
+ @Override // AbstractMasterSystemService
+ protected void onServiceRemoved(@NonNull AutofillManagerServiceImpl service,
+ @UserIdInt int userId) {
+ service.destroyLocked();
+ mAutofillCompatState.removeCompatibilityModeRequests(userId);
}
- @Override // from MasterSystemService
- protected void onServiceEnabledLocked(@NonNull AutofillManagerServiceImpl service, int userId) {
+ @Override // from AbstractMasterSystemService
+ protected void onServiceEnabledLocked(@NonNull AutofillManagerServiceImpl service,
+ @UserIdInt int userId) {
addCompatibilityModeRequestsLocked(service, userId);
}
@@ -245,7 +244,7 @@
}
// Called by Shell command.
- void destroySessions(int userId, IResultReceiver receiver) {
+ void destroySessions(@UserIdInt int userId, IResultReceiver receiver) {
Slog.i(TAG, "destroySessions() for userId " + userId);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 888ad1d..6174300 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -3,6 +3,7 @@
aidl: {
include_dirs: [
+ "frameworks/base/cmds/idmap2/idmap2d/aidl",
"frameworks/native/aidl/binder",
"frameworks/native/cmds/dumpstate/binder",
"system/core/storaged/binder",
@@ -13,6 +14,7 @@
srcs: [
"java/**/*.java",
":dumpstate_aidl",
+ ":idmap2_aidl",
":netd_aidl",
":netd_metrics_aidl",
":installd_aidl",
diff --git a/services/core/java/com/android/server/AbstractMasterSystemService.java b/services/core/java/com/android/server/AbstractMasterSystemService.java
index c955daf..6cae887 100644
--- a/services/core/java/com/android/server/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/AbstractMasterSystemService.java
@@ -244,7 +244,7 @@
*/
@GuardedBy("mLock")
@Nullable
- protected S peekServiceForUserLocked(int userId) {
+ protected S peekServiceForUserLocked(@UserIdInt int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
return mServicesCache.get(resolvedUserId);
@@ -254,7 +254,7 @@
* Updates a cached service for a given user.
*/
@GuardedBy("mLock")
- protected void updateCachedServiceLocked(int userId) {
+ protected void updateCachedServiceLocked(@UserIdInt int userId) {
updateCachedServiceLocked(userId, isDisabledLocked(userId));
}
@@ -262,7 +262,7 @@
* Checks whether the service is disabled (through {@link UserManager} restrictions) for the
* given user.
*/
- protected boolean isDisabledLocked(int userId) {
+ protected boolean isDisabledLocked(@UserIdInt int userId) {
return mDisabledUsers == null ? false : mDisabledUsers.get(userId);
}
@@ -274,7 +274,7 @@
* @return service for the user.
*/
@GuardedBy("mLock")
- protected S updateCachedServiceLocked(int userId, boolean disabled) {
+ protected S updateCachedServiceLocked(@UserIdInt int userId, boolean disabled) {
final S service = getServiceForUserLocked(userId);
if (service != null) {
service.updateLocked(disabled);
@@ -304,7 +304,7 @@
* <p>By default doesn't do anything, but can be overridden by subclasses.
*/
@SuppressWarnings("unused")
- protected void onServiceEnabledLocked(S service, @UserIdInt int userId) {
+ protected void onServiceEnabledLocked(@NonNull S service, @UserIdInt int userId) {
}
/**
@@ -314,15 +314,23 @@
*/
@GuardedBy("mLock")
@NonNull
- protected S removeCachedServiceLocked(@UserIdInt int userId) {
+ private S removeCachedServiceLocked(@UserIdInt int userId) {
final S service = peekServiceForUserLocked(userId);
if (service != null) {
mServicesCache.delete(userId);
+ onServiceRemoved(service, userId);
}
return service;
}
/**
+ * Called after the service is removed from the cache.
+ */
+ @SuppressWarnings("unused")
+ protected void onServiceRemoved(@NonNull S service, @UserIdInt int userId) {
+ }
+
+ /**
* Visits all services in the cache.
*/
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/AbstractPerUserSystemService.java
index 201abe6..97977df 100644
--- a/services/core/java/com/android/server/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/AbstractPerUserSystemService.java
@@ -167,7 +167,7 @@
@GuardedBy("mLock")
protected final int getServiceUidLocked() {
if (mServiceInfo == null) {
- Slog.w(mTag, "getServiceUidLocked(): no mServiceInfo");
+ if (mMaster.verbose) Slog.v(mTag, "getServiceUidLocked(): no mServiceInfo");
return Process.INVALID_UID;
}
return mServiceInfo.applicationInfo.uid;
@@ -267,8 +267,18 @@
@GuardedBy("mLock")
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("User: "); pw.println(mUserId);
- pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+ pw.print(prefix); pw.print("Disabled by UserManager: "); pw.println(mDisabled);
pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
- pw.print(prefix); pw.print("Service name: "); pw.println(getComponentNameFromSettings());
+ if (mServiceInfo != null) {
+ pw.print(prefix); pw.print("Service UID: ");
+ pw.println(mServiceInfo.applicationInfo.uid);
+ }
+ final String componentName = getComponentNameFromSettings();
+ if (componentName != null) {
+ pw.print(prefix); pw.print("Service name: ");
+ pw.println(componentName);
+ } else {
+ pw.println("No service package set");
+ }
}
}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 5814064..fa98da5 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -80,6 +80,7 @@
import android.util.TimeUtils;
import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsActiveCallback;
import com.android.internal.app.IAppOpsCallback;
@@ -219,6 +220,7 @@
SparseIntArray mProfileOwners;
+ @GuardedBy("this")
private CheckOpsDelegate mCheckOpsDelegate;
/**
@@ -1589,24 +1591,28 @@
public int checkOperation(int code, int uid, String packageName) {
final CheckOpsDelegate delegate;
synchronized (this) {
- if (mCheckOpsDelegate == null) {
- return checkOperationImpl(code, uid, packageName);
- }
delegate = mCheckOpsDelegate;
}
+ if (delegate == null) {
+ return checkOperationImpl(code, uid, packageName);
+ }
return delegate.checkOperation(code, uid, packageName,
AppOpsService.this::checkOperationImpl);
}
private int checkOperationImpl(int code, int uid, String packageName) {
+ verifyIncomingUid(uid);
+ verifyIncomingOp(code);
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ return checkOperationUnchecked(code, uid, resolvedPackageName);
+ }
+
+ private int checkOperationUnchecked(int code, int uid, String packageName) {
synchronized (this) {
- verifyIncomingUid(uid);
- verifyIncomingOp(code);
- String resolvedPackageName = resolvePackageName(uid, packageName);
- if (resolvedPackageName == null) {
- return AppOpsManager.MODE_IGNORED;
- }
- if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
+ if (isOpRestrictedLocked(uid, code, packageName)) {
return AppOpsManager.MODE_IGNORED;
}
code = AppOpsManager.opToSwitch(code);
@@ -1615,7 +1621,7 @@
&& uidState.opModes.indexOfKey(code) >= 0) {
return uidState.opModes.get(code);
}
- Op op = getOpLocked(code, uid, resolvedPackageName, false, true, false);
+ Op op = getOpLocked(code, uid, packageName, false, true, false);
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
@@ -1627,31 +1633,31 @@
public int checkAudioOperation(int code, int usage, int uid, String packageName) {
final CheckOpsDelegate delegate;
synchronized (this) {
- if (mCheckOpsDelegate == null) {
- return checkAudioOperationImpl(code, usage, uid, packageName);
- }
delegate = mCheckOpsDelegate;
}
+ if (delegate == null) {
+ return checkAudioOperationImpl(code, usage, uid, packageName);
+ }
return delegate.checkAudioOperation(code, usage, uid, packageName,
AppOpsService.this::checkAudioOperationImpl);
}
private int checkAudioOperationImpl(int code, int usage, int uid, String packageName) {
+ boolean suspended;
+ try {
+ suspended = isPackageSuspendedForUser(packageName, uid);
+ } catch (IllegalArgumentException ex) {
+ // Package not found.
+ suspended = false;
+ }
+
+ if (suspended) {
+ Slog.i(TAG, "Audio disabled for suspended package=" + packageName
+ + " for uid=" + uid);
+ return AppOpsManager.MODE_IGNORED;
+ }
+
synchronized (this) {
- boolean suspended;
- try {
- suspended = isPackageSuspendedForUser(packageName, uid);
- } catch (IllegalArgumentException ex) {
- // Package not found.
- suspended = false;
- }
-
- if (suspended) {
- Slog.i(TAG, "Audio disabled for suspended package=" + packageName
- + " for uid=" + uid);
- return AppOpsManager.MODE_IGNORED;
- }
-
final int mode = checkRestrictionLocked(code, usage, uid, packageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
return mode;
@@ -1754,11 +1760,11 @@
public int noteOperation(int code, int uid, String packageName) {
final CheckOpsDelegate delegate;
synchronized (this) {
- if (mCheckOpsDelegate == null) {
- return noteOperationImpl(code, uid, packageName);
- }
delegate = mCheckOpsDelegate;
}
+ if (delegate == null) {
+ return noteOperationImpl(code, uid, packageName);
+ }
return delegate.noteOperation(code, uid, packageName,
AppOpsService.this::noteOperationImpl);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 771d376..aa96082 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4820,6 +4820,10 @@
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes,
int flags, Bundle bOptions, int userId) {
+
+ // NOTE: The service lock isn't held in this method because nothing in the method requires
+ // the service lock to be held.
+
enforceNotIsolatedCaller("getIntentSender");
// Refuse possible leaked file descriptors
if (intents != null) {
@@ -4851,43 +4855,41 @@
}
}
- synchronized(this) {
- int callingUid = Binder.getCallingUid();
- int origUserId = userId;
- userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
- type == ActivityManager.INTENT_SENDER_BROADCAST,
- ALLOW_NON_FULL, "getIntentSender", null);
- if (origUserId == UserHandle.USER_CURRENT) {
- // We don't want to evaluate this until the pending intent is
- // actually executed. However, we do want to always do the
- // security checking for it above.
- userId = UserHandle.USER_CURRENT;
- }
- try {
- if (callingUid != 0 && callingUid != SYSTEM_UID) {
- final int uid = AppGlobals.getPackageManager().getPackageUid(packageName,
- MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callingUid));
- if (!UserHandle.isSameApp(callingUid, uid)) {
- String msg = "Permission Denial: getIntentSender() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + ", (need uid=" + uid + ")"
- + " is not allowed to send as package " + packageName;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
+ int callingUid = Binder.getCallingUid();
+ int origUserId = userId;
+ userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
+ type == ActivityManager.INTENT_SENDER_BROADCAST,
+ ALLOW_NON_FULL, "getIntentSender", null);
+ if (origUserId == UserHandle.USER_CURRENT) {
+ // We don't want to evaluate this until the pending intent is
+ // actually executed. However, we do want to always do the
+ // security checking for it above.
+ userId = UserHandle.USER_CURRENT;
+ }
+ try {
+ if (callingUid != 0 && callingUid != SYSTEM_UID) {
+ final int uid = AppGlobals.getPackageManager().getPackageUid(packageName,
+ MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callingUid));
+ if (!UserHandle.isSameApp(callingUid, uid)) {
+ String msg = "Permission Denial: getIntentSender() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + ", (need uid=" + uid + ")"
+ + " is not allowed to send as package " + packageName;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
}
+ }
- if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
- return mAtmInternal.getIntentSender(type, packageName, callingUid, userId,
- token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
- }
- return mPendingIntentController.getIntentSender(type, packageName, callingUid,
- userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
- bOptions);
- } catch (RemoteException e) {
- throw new SecurityException(e);
+ if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
+ return mAtmInternal.getIntentSender(type, packageName, callingUid, userId,
+ token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
}
+ return mPendingIntentController.getIntentSender(type, packageName, callingUid,
+ userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
+ bOptions);
+ } catch (RemoteException e) {
+ throw new SecurityException(e);
}
}
@@ -7002,7 +7004,7 @@
mCoreSettingsObserver = new CoreSettingsObserver(this);
mActivityTaskManager.installSystemProviders();
mDevelopmentSettingsObserver = new DevelopmentSettingsObserver();
- GlobalSettingsToPropertiesMapper.start(mContext.getContentResolver());
+ SettingsToPropertiesMapper.start(mContext.getContentResolver());
// Now that the settings provider is published we can consider sending
// in a rescue party.
diff --git a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
deleted file mode 100644
index 1366c21..0000000
--- a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.am;
-
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Build;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Slog;
-import android.view.ThreadedRenderer;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-
-/**
- * Maps global system settings to system properties.
- * <p>The properties are dynamically updated when settings change.
- */
-class GlobalSettingsToPropertiesMapper {
-
- private static final String TAG = "GlobalSettingsToPropertiesMapper";
-
- // List mapping entries in the following format:
- // {Settings.Global.SETTING_NAME, "system_property_name"}
- // Important: Property being added should be whitelisted by SELinux policy or have one of the
- // already whitelisted prefixes in system_server.te, e.g. sys.
- private static final String[][] sGlobalSettingsMapping = new String[][] {
- {Settings.Global.SYS_VDSO, "sys.vdso"},
- {Settings.Global.FPS_DEVISOR, ThreadedRenderer.DEBUG_FPS_DIVISOR},
- {Settings.Global.DISPLAY_PANEL_LPM, "sys.display_panel_lpm"},
- {Settings.Global.SYS_UIDCPUPOWER, "sys.uidcpupower"},
- {Settings.Global.SYS_TRACED, "sys.traced.enable_override"},
- };
-
-
- private final ContentResolver mContentResolver;
- private final String[][] mGlobalSettingsMapping;
-
- @VisibleForTesting
- GlobalSettingsToPropertiesMapper(ContentResolver contentResolver,
- String[][] globalSettingsMapping) {
- mContentResolver = contentResolver;
- mGlobalSettingsMapping = globalSettingsMapping;
- }
-
- void updatePropertiesFromGlobalSettings() {
- for (String[] entry : mGlobalSettingsMapping) {
- final String settingName = entry[0];
- final String propName = entry[1];
- Uri settingUri = Settings.Global.getUriFor(settingName);
- Preconditions.checkNotNull(settingUri, "Setting " + settingName + " not found");
- ContentObserver co = new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- updatePropertyFromSetting(settingName, propName);
- }
- };
- updatePropertyFromSetting(settingName, propName);
- mContentResolver.registerContentObserver(settingUri, false, co);
- }
- }
-
- public static void start(ContentResolver contentResolver) {
- new GlobalSettingsToPropertiesMapper(contentResolver, sGlobalSettingsMapping)
- .updatePropertiesFromGlobalSettings();
- }
-
- private String getGlobalSetting(String name) {
- return Settings.Global.getString(mContentResolver, name);
- }
-
- private void setProperty(String key, String value) {
- // Check if need to clear the property
- if (value == null) {
- // It's impossible to remove system property, therefore we check previous value to
- // avoid setting an empty string if the property wasn't set.
- if (TextUtils.isEmpty(systemPropertiesGet(key))) {
- return;
- }
- value = "";
- }
- try {
- systemPropertiesSet(key, value);
- } catch (Exception e) {
- // Failure to set a property can be caused by SELinux denial. This usually indicates
- // that the property wasn't whitelisted in sepolicy.
- // No need to report it on all user devices, only on debug builds.
- if (Build.IS_DEBUGGABLE) {
- Slog.wtf(TAG, "Unable to set property " + key + " value '" + value + "'", e);
- } else {
- Slog.e(TAG, "Unable to set property " + key + " value '" + value + "'", e);
- }
- }
- }
-
- @VisibleForTesting
- protected String systemPropertiesGet(String key) {
- return SystemProperties.get(key);
- }
-
- @VisibleForTesting
- protected void systemPropertiesSet(String key, String value) {
- SystemProperties.set(key, value);
- }
-
- @VisibleForTesting
- void updatePropertyFromSetting(String settingName, String propName) {
- String settingValue = getGlobalSetting(settingName);
- setProperty(propName, settingValue);
- }
-}
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 79c98e5..5208ca5 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -27,4 +27,4 @@
michaelwr@google.com
narayan@google.com
-per-file GlobalSettingsToPropertiesMapper.java = omakoto@google.com, svetoslavganov@google.com, yamasani@google.com
+per-file SettingsToPropertiesMapper.java = omakoto@google.com, svetoslavganov@google.com, yamasani@google.com
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
new file mode 100644
index 0000000..a5848ca
--- /dev/null
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -0,0 +1,265 @@
+/*
+ * 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.am;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashSet;
+
+/**
+ * Maps system settings to system properties.
+ * <p>The properties are dynamically updated when settings change.
+ */
+class SettingsToPropertiesMapper {
+
+ private static final String TAG = "SettingsToPropertiesMapper";
+
+ private static final String SYSTEM_PROPERTY_PREFIX = "persist.device_config.";
+
+ private static final String RESET_PERFORMED_PROPERTY = "device_config.reset_performed";
+
+ private static final String RESET_RECORD_FILE_PATH =
+ "/data/server_configurable_flags/reset_flags";
+
+ private static final String SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX = "^[\\w\\.\\-@:]*$";
+
+ private static final String SYSTEM_PROPERTY_INVALID_SUBSTRING = "..";
+
+ private static final int SYSTEM_PROPERTY_MAX_LENGTH = 92;
+
+ // experiment flags added to Global.Settings(before new "Config" provider table is available)
+ // will be added under this category.
+ private static final String GLOBAL_SETTINGS_CATEGORY = "global_settings";
+
+ // Add the global setting you want to push to native level as experiment flag into this list.
+ //
+ // NOTE: please grant write permission system property prefix
+ // with format persist.experiment.[experiment_category_name]. in system_server.te and grant read
+ // permission in the corresponding .te file your feature belongs to.
+ @VisibleForTesting
+ static final String[] sGlobalSettings = new String[] {
+ };
+
+ @VisibleForTesting
+ static final String[] sDeviceConfigScopes = new String[] {
+ };
+
+ private final String[] mGlobalSettings;
+
+ private final String[] mDeviceConfigScopes;
+
+ private final ContentResolver mContentResolver;
+
+ @VisibleForTesting
+ protected SettingsToPropertiesMapper(ContentResolver contentResolver,
+ String[] globalSettings,
+ String[] deviceConfigScopes) {
+ mContentResolver = contentResolver;
+ mGlobalSettings = globalSettings;
+ mDeviceConfigScopes = deviceConfigScopes;
+ }
+
+ @VisibleForTesting
+ void updatePropertiesFromSettings() {
+ for (String globalSetting : mGlobalSettings) {
+ Uri settingUri = Settings.Global.getUriFor(globalSetting);
+ String propName = makePropertyName(GLOBAL_SETTINGS_CATEGORY, globalSetting);
+ if (settingUri == null) {
+ log("setting uri is null for globalSetting " + globalSetting);
+ continue;
+ }
+ if (propName == null) {
+ log("invalid prop name for globalSetting " + globalSetting);
+ continue;
+ }
+
+ ContentObserver co = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updatePropertyFromSetting(globalSetting, propName, true);
+ }
+ };
+
+ // only updating on starting up when no native flags reset is performed during current
+ // booting.
+ if (!isNativeFlagsResetPerformed()) {
+ updatePropertyFromSetting(globalSetting, propName, true);
+ }
+ mContentResolver.registerContentObserver(settingUri, false, co);
+ }
+
+ // TODO: address sDeviceConfigScopes after DeviceConfig APIs are available.
+ }
+
+ public static SettingsToPropertiesMapper start(ContentResolver contentResolver) {
+ SettingsToPropertiesMapper mapper = new SettingsToPropertiesMapper(
+ contentResolver, sGlobalSettings, sDeviceConfigScopes);
+ mapper.updatePropertiesFromSettings();
+ return mapper;
+ }
+
+ /**
+ * If native level flags reset has been performed as an attempt to recover from a crash loop
+ * during current device booting.
+ * @return
+ */
+ public boolean isNativeFlagsResetPerformed() {
+ String value = systemPropertiesGet(RESET_PERFORMED_PROPERTY);
+ return "true".equals(value);
+ }
+
+ /**
+ * return an array of native flag categories under which flags got reset during current device
+ * booting.
+ * @return
+ */
+ public String[] getResetNativeCategories() {
+ if (!isNativeFlagsResetPerformed()) {
+ return new String[0];
+ }
+
+ String content = getResetFlagsFileContent();
+ if (TextUtils.isEmpty(content)) {
+ return new String[0];
+ }
+
+ String[] property_names = content.split(";");
+ HashSet<String> categories = new HashSet<>();
+ for (String property_name : property_names) {
+ String[] segments = property_name.split("\\.");
+ if (segments.length < 3) {
+ log("failed to extract category name from property " + property_name);
+ continue;
+ }
+ categories.add(segments[2]);
+ }
+ return categories.toArray(new String[0]);
+ }
+
+ /**
+ * system property name constructing rule: "persist.device_config.[category_name].[flag_name]".
+ * If the name contains invalid characters or substrings for system property name,
+ * will return null.
+ * @param categoryName
+ * @param flagName
+ * @return
+ */
+ @VisibleForTesting
+ static String makePropertyName(String categoryName, String flagName) {
+ String propertyName = SYSTEM_PROPERTY_PREFIX + categoryName + "." + flagName;
+
+ if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX)
+ || propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) {
+ return null;
+ }
+
+ return propertyName;
+ }
+
+ private String getSetting(String name, boolean isGlobalSetting) {
+ if (isGlobalSetting) {
+ return Settings.Global.getString(mContentResolver, name);
+ } else {
+ // TODO: complete the code after DeviceConfig APIs implemented.
+ return null;
+ }
+ }
+
+ private void setProperty(String key, String value) {
+ // Check if need to clear the property
+ if (value == null) {
+ // It's impossible to remove system property, therefore we check previous value to
+ // avoid setting an empty string if the property wasn't set.
+ if (TextUtils.isEmpty(systemPropertiesGet(key))) {
+ return;
+ }
+ value = "";
+ } else if (value.length() > SYSTEM_PROPERTY_MAX_LENGTH) {
+ log(value + " exceeds system property max length.");
+ return;
+ }
+
+ try {
+ systemPropertiesSet(key, value);
+ } catch (Exception e) {
+ // Failure to set a property can be caused by SELinux denial. This usually indicates
+ // that the property wasn't whitelisted in sepolicy.
+ // No need to report it on all user devices, only on debug builds.
+ log("Unable to set property " + key + " value '" + value + "'", e);
+ }
+ }
+
+ private static void log(String msg, Exception e) {
+ if (Build.IS_DEBUGGABLE) {
+ Slog.wtf(TAG, msg, e);
+ } else {
+ Slog.e(TAG, msg, e);
+ }
+ }
+
+ private static void log(String msg) {
+ if (Build.IS_DEBUGGABLE) {
+ Slog.wtf(TAG, msg);
+ } else {
+ Slog.e(TAG, msg);
+ }
+ }
+
+ @VisibleForTesting
+ protected String systemPropertiesGet(String key) {
+ return SystemProperties.get(key);
+ }
+
+ @VisibleForTesting
+ protected void systemPropertiesSet(String key, String value) {
+ SystemProperties.set(key, value);
+ }
+
+ @VisibleForTesting
+ protected String getResetFlagsFileContent() {
+ String content = null;
+ try {
+ File reset_flag_file = new File(RESET_RECORD_FILE_PATH);
+ BufferedReader br = new BufferedReader(new FileReader(reset_flag_file));
+ content = br.readLine();
+
+ br.close();
+ } catch (IOException ioe) {
+ log("failed to read file " + RESET_RECORD_FILE_PATH, ioe);
+ }
+ return content;
+ }
+
+ @VisibleForTesting
+ void updatePropertyFromSetting(String settingName, String propName, boolean isGlobalSetting) {
+ String settingValue = getSetting(settingName, isGlobalSetting);
+ setProperty(propName, settingValue);
+ }
+}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index a769590..65537ad 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -55,6 +55,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.intelligence.IntelligenceManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -157,6 +158,7 @@
private final IUserManager mUm;
private final PackageManager mPm;
private final AppOpsManager mAppOps;
+ private final IntelligenceManagerInternal mIm;
private final IBinder mPermissionOwner;
private HostClipboardMonitor mHostClipboardMonitor = null;
private Thread mHostMonitorThread = null;
@@ -176,6 +178,7 @@
mPm = getContext().getPackageManager();
mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
+ mIm = LocalServices.getService(IntelligenceManagerInternal.class);
final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
mPermissionOwner = permOwner;
if (IS_EMULATOR) {
@@ -635,8 +638,9 @@
return true;
}
// The default IME is always allowed to access the clipboard.
+ int userId = UserHandle.getUserId(callingUid);
String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD, UserHandle.getUserId(callingUid));
+ Settings.Secure.DEFAULT_INPUT_METHOD, userId);
if (!TextUtils.isEmpty(defaultIme)) {
final String imePkg = ComponentName.unflattenFromString(defaultIme).getPackageName();
if (imePkg.equals(callingPackage)) {
@@ -646,13 +650,18 @@
switch (op) {
case AppOpsManager.OP_READ_CLIPBOARD:
- // Clipboard can only be read by applications with focus.
- boolean uidFocused = mWm.isUidFocused(callingUid);
- if (!uidFocused) {
- Slog.e(TAG, "Denying clipboard access to " + callingPackage
- + ", application is not in focus.");
+ // Clipboard can only be read by applications with focus..
+ boolean allowed = mWm.isUidFocused(callingUid);
+ if (!allowed && mIm != null) {
+ // ...or the Intelligence Service
+ allowed = mIm.isIntelligenceServiceForUser(callingUid, userId);
}
- return uidFocused;
+ if (!allowed) {
+ Slog.e(TAG, "Denying clipboard access to " + callingPackage
+ + ", application is not in focus neither is the IntelligeService for "
+ + "user " + userId);
+ }
+ return allowed;
case AppOpsManager.OP_WRITE_CLIPBOARD:
// Writing is allowed without focus.
return true;
diff --git a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
new file mode 100644
index 0000000..0ed56ff
--- /dev/null
+++ b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
@@ -0,0 +1,33 @@
+/*
+ * 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.intelligence;
+
+import android.annotation.UserIdInt;
+
+/**
+ * Intelligence Manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class IntelligenceManagerInternal {
+
+ /**
+ * Checks whether the given {@code uid} owns the
+ * {@link android.service.intelligence.IntelligenceService} implementation associated with the
+ * given {@code userId}.
+ */
+ public abstract boolean isIntelligenceServiceForUser(int uid, @UserIdInt int userId);
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1c7572e..cfbe83d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -885,6 +885,9 @@
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
r.recordDirectReplied();
+ mMetricsLogger.write(r.getLogMaker()
+ .setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
+ .setType(MetricsEvent.TYPE_ACTION));
reportUserInteraction(r);
}
}
@@ -1160,6 +1163,7 @@
mConditionProviders.onUserSwitched(userId);
mListeners.onUserSwitched(userId);
mZenModeHelper.onUserSwitched(userId);
+ mPreferencesHelper.onUserSwitched(userId);
}
// assistant is the only thing that cares about managed profiles specifically
mAssistants.onUserSwitched(userId);
@@ -1188,6 +1192,7 @@
mConditionProviders.onUserUnlocked(userId);
mListeners.onUserUnlocked(userId);
mZenModeHelper.onUserUnlocked(userId);
+ mPreferencesHelper.onUserUnlocked(userId);
}
}
}
@@ -2525,6 +2530,19 @@
}
@Override
+ public int getAppsBypassingDndCount(int userId) {
+ checkCallerIsSystem();
+ return mPreferencesHelper.getAppsBypassingDndCount(userId);
+ }
+
+ @Override
+ public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(
+ String pkg, int userId) {
+ checkCallerIsSystem();
+ return mPreferencesHelper.getNotificationChannelsBypassingDnd(pkg, userId);
+ }
+
+ @Override
public boolean areChannelsBypassingDnd() {
return mPreferencesHelper.areChannelsBypassingDnd();
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 8fce5e3..fd65ebe 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -111,7 +111,6 @@
// pkg => PackagePreferences
private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
-
private final Context mContext;
private final PackageManager mPm;
private final RankingHandler mRankingHandler;
@@ -120,7 +119,6 @@
private SparseBooleanArray mBadgingEnabled;
private boolean mAreChannelsBypassingDnd;
-
public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
ZenModeHelper zenHelper) {
mContext = context;
@@ -129,11 +127,7 @@
mPm = pm;
updateBadgingEnabled();
-
- mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state &
- NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
- updateChannelsBypassingDnd();
-
+ syncChannelsBypassingDnd(mContext.getUserId());
}
public void readXml(XmlPullParser parser, boolean forRestore)
@@ -525,6 +519,7 @@
// but the system can
if (group.isBlocked() != oldGroup.isBlocked()) {
group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
+ updateChannelsBypassingDnd(mContext.getUserId());
}
if (group.canOverlayApps() != oldGroup.canOverlayApps()) {
group.lockFields(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY);
@@ -571,6 +566,7 @@
// Apps are allowed to downgrade channel importance if the user has not changed any
// fields on this channel yet.
+ final int previousExistingImportance = existing.getImportance();
if (existing.getUserLockedFields() == 0 &&
channel.getImportance() < existing.getImportance()) {
existing.setImportance(channel.getImportance());
@@ -582,8 +578,9 @@
boolean bypassDnd = channel.canBypassDnd();
existing.setBypassDnd(bypassDnd);
- if (bypassDnd != mAreChannelsBypassingDnd) {
- updateChannelsBypassingDnd();
+ if (bypassDnd != mAreChannelsBypassingDnd
+ || previousExistingImportance != existing.getImportance()) {
+ updateChannelsBypassingDnd(mContext.getUserId());
}
}
@@ -613,7 +610,7 @@
r.channels.put(channel.getId(), channel);
if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
- updateChannelsBypassingDnd();
+ updateChannelsBypassingDnd(mContext.getUserId());
}
MetricsLogger.action(getChannelLog(channel, pkg).setType(
com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
@@ -663,8 +660,9 @@
MetricsLogger.action(getChannelLog(updatedChannel, pkg));
}
- if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd) {
- updateChannelsBypassingDnd();
+ if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
+ || channel.getImportance() != updatedChannel.getImportance()) {
+ updateChannelsBypassingDnd(mContext.getUserId());
}
updateConfig();
}
@@ -701,7 +699,7 @@
MetricsLogger.action(lm);
if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
- updateChannelsBypassingDnd();
+ updateChannelsBypassingDnd(mContext.getUserId());
}
}
}
@@ -859,6 +857,27 @@
}
/**
+ * Gets all notification channels associated with the given pkg and userId that can bypass dnd
+ */
+ public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
+ int userId) {
+ List<NotificationChannel> channels = new ArrayList<>();
+ synchronized (mPackagePreferences) {
+ final PackagePreferences r = mPackagePreferences.get(
+ packagePreferencesKey(pkg, userId));
+ // notifications from this package aren't blocked
+ if (r != null && r.importance != IMPORTANCE_NONE) {
+ for (NotificationChannel channel : r.channels.values()) {
+ if (channelIsLive(r, channel) && channel.canBypassDnd()) {
+ channels.add(channel);
+ }
+ }
+ }
+ }
+ return new ParceledListSlice<>(channels);
+ }
+
+ /**
* True for pre-O apps that only have the default channel, or pre O apps that have no
* channels yet. This method will create the default channel for pre-O apps that don't have it.
* Should never be true for O+ targeting apps, but that's enforced on boot/when an app
@@ -922,18 +941,62 @@
return count;
}
- public void updateChannelsBypassingDnd() {
+ /**
+ * Returns the number of apps that have at least one notification channel that can bypass DND
+ * for given particular user
+ */
+ public int getAppsBypassingDndCount(int userId) {
+ int count = 0;
synchronized (mPackagePreferences) {
- final int numPackagePreferencess = mPackagePreferences.size();
- for (int PackagePreferencesIndex = 0; PackagePreferencesIndex < numPackagePreferencess;
- PackagePreferencesIndex++) {
- final PackagePreferences r = mPackagePreferences.valueAt(PackagePreferencesIndex);
- final int numChannels = r.channels.size();
+ final int numPackagePreferences = mPackagePreferences.size();
+ for (int i = 0; i < numPackagePreferences; i++) {
+ final PackagePreferences r = mPackagePreferences.valueAt(i);
+ // Package isn't associated with this userId or notifications from this package are
+ // blocked
+ if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
+ continue;
+ }
- for (int channelIndex = 0; channelIndex < numChannels; channelIndex++) {
- NotificationChannel channel = r.channels.valueAt(channelIndex);
- if (!channel.isDeleted() && channel.canBypassDnd()) {
- // If any channel bypasses DND, synchronize state and return early.
+ for (NotificationChannel channel : r.channels.values()) {
+ if (channelIsLive(r, channel) && channel.canBypassDnd()) {
+ count++;
+ break;
+ }
+ }
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before
+ * updating
+ * @param userId
+ */
+ private void syncChannelsBypassingDnd(int userId) {
+ mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
+ & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
+ updateChannelsBypassingDnd(userId);
+ }
+
+ /**
+ * Updates the user's NotificationPolicy based on whether the given userId
+ * has channels bypassing DND
+ * @param userId
+ */
+ private void updateChannelsBypassingDnd(int userId) {
+ synchronized (mPackagePreferences) {
+ final int numPackagePreferences = mPackagePreferences.size();
+ for (int i = 0; i < numPackagePreferences; i++) {
+ final PackagePreferences r = mPackagePreferences.valueAt(i);
+ // Package isn't associated with this userId or notifications from this package are
+ // blocked
+ if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
+ continue;
+ }
+
+ for (NotificationChannel channel : r.channels.values()) {
+ if (channelIsLive(r, channel) && channel.canBypassDnd()) {
if (!mAreChannelsBypassingDnd) {
mAreChannelsBypassingDnd = true;
updateZenPolicy(true);
@@ -943,7 +1006,6 @@
}
}
}
-
// If no channels bypass DND, update the zen policy once to disable DND bypass.
if (mAreChannelsBypassingDnd) {
mAreChannelsBypassingDnd = false;
@@ -951,6 +1013,22 @@
}
}
+ private boolean channelIsLive(PackagePreferences pkgPref, NotificationChannel channel) {
+ // Channel is in a group that's blocked
+ if (!TextUtils.isEmpty(channel.getGroup())) {
+ if (pkgPref.groups.get(channel.getGroup()).isBlocked()) {
+ return false;
+ }
+ }
+
+ // Channel is deleted or is blocked
+ if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) {
+ return false;
+ }
+
+ return true;
+ }
+
public void updateZenPolicy(boolean areChannelsBypassingDnd) {
NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
@@ -1329,6 +1407,20 @@
return packageChannels;
}
+ /**
+ * Called when user switches
+ */
+ public void onUserSwitched(int userId) {
+ syncChannelsBypassingDnd(userId);
+ }
+
+ /**
+ * Called when user is unlocked
+ */
+ public void onUserUnlocked(int userId) {
+ syncChannelsBypassingDnd(userId);
+ }
+
public void onUserRemoved(int userId) {
synchronized (mPackagePreferences) {
int N = mPackagePreferences.size();
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 807c343..731e6bc 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -16,36 +16,46 @@
package com.android.server.om;
+import static android.content.Context.IDMAP_SERVICE;
+import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+
import static com.android.server.om.OverlayManagerService.DEBUG;
import static com.android.server.om.OverlayManagerService.TAG;
import android.annotation.NonNull;
import android.content.om.OverlayInfo;
import android.content.pm.PackageInfo;
+import android.os.IBinder;
+import android.os.IIdmap2;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Slog;
-import com.android.server.pm.Installer.InstallerException;
+import com.android.internal.os.BackgroundThread;
import com.android.server.pm.Installer;
-import java.io.DataInputStream;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
/**
* Handle the creation and deletion of idmap files.
*
* The actual work is performed by the idmap binary, launched through Installer
- * and installd.
+ * and installd (or idmap2).
*
* Note: this class is subclassed in the OMS unit tests, and hence not marked as final.
*/
class IdmapManager {
+ private static final boolean FEATURE_FLAG_IDMAP2 = false;
+
private final Installer mInstaller;
+ private IIdmap2 mIdmap2Service;
IdmapManager(final Installer installer) {
mInstaller = installer;
+ if (FEATURE_FLAG_IDMAP2) {
+ connectToIdmap2d();
+ }
}
boolean createIdmap(@NonNull final PackageInfo targetPackage,
@@ -59,8 +69,12 @@
final String targetPath = targetPackage.applicationInfo.getBaseCodePath();
final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
try {
- mInstaller.idmap(targetPath, overlayPath, sharedGid);
- } catch (InstallerException e) {
+ if (FEATURE_FLAG_IDMAP2) {
+ mIdmap2Service.createIdmap(targetPath, overlayPath, userId);
+ } else {
+ mInstaller.idmap(targetPath, overlayPath, sharedGid);
+ }
+ } catch (Exception e) {
Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
+ overlayPath + ": " + e.getMessage());
return false;
@@ -69,13 +83,16 @@
}
boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
- // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
if (DEBUG) {
Slog.d(TAG, "remove idmap for " + oi.baseCodePath);
}
try {
- mInstaller.removeIdmap(oi.baseCodePath);
- } catch (InstallerException e) {
+ if (FEATURE_FLAG_IDMAP2) {
+ mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
+ } else {
+ mInstaller.removeIdmap(oi.baseCodePath);
+ }
+ } catch (Exception e) {
Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage());
return false;
}
@@ -83,19 +100,58 @@
}
boolean idmapExists(@NonNull final OverlayInfo oi) {
- // unused OverlayInfo.userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
- return new File(getIdmapPath(oi.baseCodePath)).isFile();
+ return new File(getIdmapPath(oi.baseCodePath, oi.userId)).isFile();
}
boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) {
- // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
- return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath())).isFile();
+ return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath(), userId))
+ .isFile();
}
- private String getIdmapPath(@NonNull final String baseCodePath) {
- final StringBuilder sb = new StringBuilder("/data/resource-cache/");
- sb.append(baseCodePath.substring(1).replace('/', '@'));
- sb.append("@idmap");
- return sb.toString();
+ private @NonNull String getIdmapPath(@NonNull final String overlayPackagePath,
+ final int userId) {
+ if (FEATURE_FLAG_IDMAP2) {
+ try {
+ return mIdmap2Service.getIdmapPath(overlayPackagePath, userId);
+ } catch (Exception e) {
+ Slog.w(TAG, "failed to get idmap path for " + overlayPackagePath + ": "
+ + e.getMessage());
+ return "";
+ }
+ } else {
+ final StringBuilder sb = new StringBuilder("/data/resource-cache/");
+ sb.append(overlayPackagePath.substring(1).replace('/', '@'));
+ sb.append("@idmap");
+ return sb.toString();
+ }
+ }
+
+ private void connectToIdmap2d() {
+ IBinder binder = ServiceManager.getService(IDMAP_SERVICE);
+ if (binder != null) {
+ try {
+ binder.linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ Slog.w(TAG, "service '" + IDMAP_SERVICE + "' died; reconnecting...");
+ connectToIdmap2d();
+ }
+
+ }, 0);
+ } catch (RemoteException e) {
+ binder = null;
+ }
+ }
+ if (binder != null) {
+ mIdmap2Service = IIdmap2.Stub.asInterface(binder);
+ if (DEBUG) {
+ Slog.d(TAG, "service '" + IDMAP_SERVICE + "' connected");
+ }
+ } else {
+ Slog.w(TAG, "service '" + IDMAP_SERVICE + "' not found; trying again...");
+ BackgroundThread.getHandler().postDelayed(() -> {
+ connectToIdmap2d();
+ }, SECOND_IN_MILLIS);
+ }
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 36bf83d..572d368 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -179,19 +179,13 @@
List<OverlayInfo> getOverlaysForTarget(@NonNull final String targetPackageName,
final int userId) {
- // Static RROs targeting "android" are loaded from AssetManager, and so they should be
- // ignored in OverlayManagerService.
return selectWhereTarget(targetPackageName, userId)
- .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName())))
.map(SettingsItem::getOverlayInfo)
.collect(Collectors.toList());
}
ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
- // Static RROs targeting "android" are loaded from AssetManager, and so they should be
- // ignored in OverlayManagerService.
return selectWhereUser(userId)
- .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName())))
.map(SettingsItem::getOverlayInfo)
.collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new,
Collectors.toList()));
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 275f3dc..b490381 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -362,7 +362,7 @@
}
private static boolean shouldShowHiddenApp(ApplicationInfo appInfo) {
- if (appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
+ if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
return false;
}
return true;
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index a55b49f..f78d263 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -20,7 +20,7 @@
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.security.IKeystoreService;
+import android.security.keystore.IKeystoreService;
import android.util.Slog;
import com.android.internal.policy.IKeyguardService;
diff --git a/services/core/java/com/android/server/role/RemoteRoleControllerService.java b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
index b670291..7d34270 100644
--- a/services/core/java/com/android/server/role/RemoteRoleControllerService.java
+++ b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
@@ -16,10 +16,10 @@
package com.android.server.role;
-import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
import android.app.role.IRoleManagerCallback;
import android.app.role.RoleManagerCallback;
import android.content.ComponentName;
@@ -34,6 +34,7 @@
import android.rolecontrollerservice.RoleControllerService;
import android.util.Slog;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.function.pooled.PooledLambda;
import java.util.ArrayDeque;
@@ -44,9 +45,13 @@
*/
public class RemoteRoleControllerService {
+ static final boolean DEBUG = false;
private static final String LOG_TAG = RemoteRoleControllerService.class.getSimpleName();
@NonNull
+ private static final Handler sCallbackHandler = BackgroundThread.getHandler();
+
+ @NonNull
private final Connection mConnection;
public RemoteRoleControllerService(@UserIdInt int userId, @NonNull Context context) {
@@ -87,6 +92,16 @@
service.onClearRoleHolders(roleName, callbackDelegate), callback));
}
+ /**
+ * Performs granting of default roles and permissions and appops
+ *
+ * @see RoleControllerService#onGrantDefaultRoles(RoleManagerCallback)
+ */
+ public void onGrantDefaultRoles(@NonNull IRoleManagerCallback callback) {
+ mConnection.enqueueCall(
+ new Connection.Call(IRoleControllerService::onGrantDefaultRoles, callback));
+ }
+
private static final class Connection implements ServiceConnection {
private static final long UNBIND_DELAY_MILLIS = 15 * 1000;
@@ -106,9 +121,6 @@
private final Queue<Call> mPendingCalls = new ArrayDeque<>();
@NonNull
- private final Handler mMainHandler = Handler.getMain();
-
- @NonNull
private final Runnable mUnbindRunnable = this::unbind;
Connection(@UserIdInt int userId, @NonNull Context context) {
@@ -116,14 +128,14 @@
mContext = context;
}
- @MainThread
@Override
+ @WorkerThread
public void onServiceConnected(@NonNull ComponentName name, @NonNull IBinder service) {
mService = IRoleControllerService.Stub.asInterface(service);
executePendingCalls();
}
- @MainThread
+ @WorkerThread
private void executePendingCalls() {
while (!mPendingCalls.isEmpty()) {
Call call = mPendingCalls.poll();
@@ -132,26 +144,33 @@
scheduleUnbind();
}
- @MainThread
@Override
+ @WorkerThread
public void onServiceDisconnected(@NonNull ComponentName name) {
mService = null;
}
- @MainThread
@Override
+ @WorkerThread
public void onBindingDied(@NonNull ComponentName name) {
unbind();
}
public void enqueueCall(@NonNull Call call) {
- mMainHandler.post(PooledLambda.obtainRunnable(this::executeCall, call));
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Enqueue " + call);
+ }
+ sCallbackHandler.executeOrSendMessage(PooledLambda.obtainMessage(
+ Connection::executeCall, this, call));
}
- @MainThread
+ @WorkerThread
private void executeCall(@NonNull Call call) {
ensureBound();
if (mService == null) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Delaying until service connected: " + call);
+ }
mPendingCalls.offer(call);
return;
}
@@ -159,24 +178,28 @@
scheduleUnbind();
}
- @MainThread
+ @WorkerThread
private void ensureBound() {
- mMainHandler.removeCallbacks(mUnbindRunnable);
+ sCallbackHandler.removeCallbacks(mUnbindRunnable);
if (!mBound) {
Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE);
intent.setPackage(mContext.getPackageManager()
.getPermissionControllerPackageName());
+ // Use direct handler to ensure onServiceConnected callback happens in the same
+ // call frame, as required by onGrantDefaultRoles
+ //
+ // Note that as a result, onServiceConnected may happen not on main thread!
mBound = mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
- UserHandle.of(mUserId));
+ sCallbackHandler, UserHandle.of(mUserId));
}
}
private void scheduleUnbind() {
- mMainHandler.removeCallbacks(mUnbindRunnable);
- mMainHandler.postDelayed(mUnbindRunnable, UNBIND_DELAY_MILLIS);
+ sCallbackHandler.removeCallbacks(mUnbindRunnable);
+ sCallbackHandler.postDelayed(mUnbindRunnable, UNBIND_DELAY_MILLIS);
}
- @MainThread
+ @WorkerThread
private void unbind() {
if (mBound) {
mService = null;
@@ -196,9 +219,6 @@
private final IRoleManagerCallback mCallback;
@NonNull
- private final Handler mMainHandler = Handler.getMain();
-
- @NonNull
private final Runnable mTimeoutRunnable = () -> notifyCallback(false);
private boolean mCallbackNotified;
@@ -209,10 +229,13 @@
mCallback = callback;
}
- @MainThread
+ @WorkerThread
public void execute(IRoleControllerService service) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Executing " + this);
+ }
try {
- mMainHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MILLIS);
+ sCallbackHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MILLIS);
mCallExecutor.execute(service, new CallbackDelegate());
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Error calling RoleControllerService", e);
@@ -220,13 +243,13 @@
}
}
- @MainThread
+ @WorkerThread
private void notifyCallback(boolean success) {
if (mCallbackNotified) {
return;
}
mCallbackNotified = true;
- mMainHandler.removeCallbacks(mTimeoutRunnable);
+ sCallbackHandler.removeCallbacks(mTimeoutRunnable);
try {
if (success) {
mCallback.onSuccess();
@@ -239,10 +262,15 @@
}
}
+ @Override
+ public String toString() {
+ return "Call with callback: " + mCallback;
+ }
+
@FunctionalInterface
public interface CallExecutor {
- @MainThread
+ @WorkerThread
void execute(IRoleControllerService service, IRoleManagerCallback callbackDelegate)
throws RemoteException;
}
@@ -251,13 +279,14 @@
@Override
public void onSuccess() throws RemoteException {
- mMainHandler.post(PooledLambda.obtainRunnable(Call.this::notifyCallback, true));
+ sCallbackHandler.sendMessage(PooledLambda.obtainMessage(
+ Call::notifyCallback, Call.this, true));
}
@Override
public void onFailure() throws RemoteException {
- mMainHandler.post(PooledLambda.obtainRunnable(Call.this::notifyCallback,
- false));
+ sCallbackHandler.sendMessage(PooledLambda.obtainMessage(
+ Call::notifyCallback, Call.this, false));
}
}
}
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index ded075d..d01e762 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -45,6 +45,10 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* Service for role management.
@@ -105,17 +109,37 @@
public void onStart() {
publishBinderService(Context.ROLE_SERVICE, new Stub());
//TODO add watch for new user creation and run default grants for them
+ //TODO add package update watch to detect PermissionController upgrade and run def. grants
}
@Override
public void onStartUser(@UserIdInt int userId) {
synchronized (mLock) {
//TODO only call into PermissionController if it or system upgreaded (for boot time)
- // (add package changes watch;
- // we can detect upgrade using build fingerprint and app version)
getUserStateLocked(userId);
- //TODO call permission grant policy here
+ }
+ //TODO consider calling grants only when certain conditions are met
+ // such as OS or PermissionController upgrade
+ if (RemoteRoleControllerService.DEBUG) {
Slog.i(LOG_TAG, "Granting default permissions...");
+ CompletableFuture<Void> result = new CompletableFuture<>();
+ getControllerService(userId).onGrantDefaultRoles(
+ new IRoleManagerCallback.Stub() {
+ @Override
+ public void onSuccess() {
+ result.complete(null);
+ }
+
+ @Override
+ public void onFailure() {
+ result.completeExceptionally(new RuntimeException());
+ }
+ });
+ try {
+ result.get(5, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Slog.e(LOG_TAG, "Failed to grant defaults for user " + userId, e);
+ }
}
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 01d02d6..3050409 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -47,6 +47,7 @@
import android.net.NetworkStats;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiActivityEnergyInfo;
+import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.Bundle;
@@ -87,6 +88,8 @@
import com.android.internal.app.procstats.IProcessStats;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BinderCallsStats.ExportedCallStat;
import com.android.internal.os.KernelCpuSpeedReader;
import com.android.internal.os.KernelCpuThreadReader;
@@ -205,6 +208,10 @@
@Nullable
private final KernelCpuThreadReader mKernelCpuThreadReader;
+ private BatteryStatsHelper mBatteryStatsHelper = null;
+ private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
+ private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS;
+
private static IThermalService sThermalService;
private File mBaseDir =
new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
@@ -366,6 +373,8 @@
List<Integer> uids = new ArrayList<>();
List<Long> versions = new ArrayList<>();
List<String> apps = new ArrayList<>();
+ List<String> versionStrings = new ArrayList<>();
+ List<String> installers = new ArrayList<>();
// Add in all the apps for every user/profile.
for (UserInfo profile : users) {
@@ -373,14 +382,24 @@
pm.getInstalledPackagesAsUser(PackageManager.MATCH_KNOWN_PACKAGES, profile.id);
for (int j = 0; j < pi.size(); j++) {
if (pi.get(j).applicationInfo != null) {
+ String installer;
+ try {
+ installer = pm.getInstallerPackageName(pi.get(j).packageName);
+ } catch (IllegalArgumentException e) {
+ installer = "";
+ }
+ installers.add(installer == null ? "" : installer);
uids.add(pi.get(j).applicationInfo.uid);
versions.add(pi.get(j).getLongVersionCode());
+ versionStrings.add(pi.get(j).versionName);
apps.add(pi.get(j).packageName);
}
}
}
- sStatsd.informAllUidData(toIntArray(uids), toLongArray(versions), apps.toArray(new
- String[apps.size()]));
+ sStatsd.informAllUidData(toIntArray(uids), toLongArray(versions),
+ versionStrings.toArray(new String[versionStrings.size()]),
+ apps.toArray(new String[apps.size()]),
+ installers.toArray(new String[installers.size()]));
if (DEBUG) {
Slog.d(TAG, "Sent data for " + uids.size() + " apps");
}
@@ -422,7 +441,14 @@
int uid = b.getInt(Intent.EXTRA_UID);
String app = intent.getData().getSchemeSpecificPart();
PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
- sStatsd.informOnePackage(app, uid, pi.getLongVersionCode());
+ String installer;
+ try {
+ installer = pm.getInstallerPackageName(app);
+ } catch (IllegalArgumentException e) {
+ installer = "";
+ }
+ sStatsd.informOnePackage(app, uid, pi.getLongVersionCode(), pi.versionName,
+ installer == null ? "" : installer);
}
} catch (Exception e) {
Slog.w(TAG, "Failed to inform statsd of an app update", e);
@@ -1430,6 +1456,73 @@
pulledData.add(e);
}
+ private BatteryStatsHelper getBatteryStatsHelper() {
+ if (mBatteryStatsHelper == null) {
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly().
+ mBatteryStatsHelper = new BatteryStatsHelper(mContext, false);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ mBatteryStatsHelper.create((Bundle) null);
+ }
+ long currentTime = SystemClock.elapsedRealtime();
+ if (currentTime - mBatteryStatsHelperTimestampMs >= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) {
+ // Load BatteryStats and do all the calculations.
+ mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.USER_ALL);
+ // Calculations are done so we don't need to save the raw BatteryStats data in RAM.
+ mBatteryStatsHelper.clearStats();
+ mBatteryStatsHelperTimestampMs = currentTime;
+ }
+ return mBatteryStatsHelper;
+ }
+
+ private void pullDeviceCalculatedPowerUse(int tagId,
+ long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ BatteryStatsHelper bsHelper = getBatteryStatsHelper();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeFloat((float) bsHelper.getComputedPower());
+ pulledData.add(e);
+ }
+
+ private void pullDeviceCalculatedPowerBlameUid(int tagId,
+ long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+ if (sippers == null) {
+ return;
+ }
+ for (BatterySipper bs : sippers) {
+ if (bs.drainType != bs.drainType.APP) {
+ continue;
+ }
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(bs.uidObj.getUid());
+ e.writeFloat((float) bs.totalPowerMah);
+ pulledData.add(e);
+ }
+ }
+
+ private void pullDeviceCalculatedPowerBlameOther(int tagId,
+ long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+ if (sippers == null) {
+ return;
+ }
+ for (BatterySipper bs : sippers) {
+ if (bs.drainType == bs.drainType.APP) {
+ continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid().
+ }
+ if (bs.drainType == bs.drainType.USER) {
+ continue; // This is not supported. We purposefully calculate over USER_ALL.
+ }
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(bs.drainType.ordinal());
+ e.writeFloat((float) bs.totalPowerMah);
+ pulledData.add(e);
+ }
+ }
+
private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
@@ -1655,6 +1748,18 @@
pullCpuTimePerThreadFreq(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.DEVICE_CALCULATED_POWER_USE: {
+ pullDeviceCalculatedPowerUse(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID: {
+ pullDeviceCalculatedPowerBlameUid(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER: {
+ pullDeviceCalculatedPowerBlameOther(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 61e1414..1c08d03 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -383,7 +383,7 @@
return;
}
- if (launchedActivity != null && launchedActivity.nowVisible) {
+ if (launchedActivity != null && launchedActivity.mDrawn) {
// Launched activity is already visible. We cannot measure windows drawn delay.
reset(true /* abort */, info, "launched activity already visible");
return;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 23f8125..c43e64e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
import static android.app.WaitResult.INVALID_DELAY;
@@ -75,6 +76,25 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
+import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
+import static com.android.server.am.ActivityRecordProto.PROC_ID;
+import static com.android.server.am.ActivityRecordProto.STATE;
+import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
+import static com.android.server.am.ActivityRecordProto.VISIBLE;
+import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
+import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
+import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStack.LAUNCH_TICK;
+import static com.android.server.wm.ActivityStack.LAUNCH_TICK_MSG;
+import static com.android.server.wm.ActivityStack.PAUSE_TIMEOUT_MSG;
+import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
@@ -89,34 +109,14 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
-import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
-import static com.android.server.am.ActivityRecordProto.PROC_ID;
-import static com.android.server.am.ActivityRecordProto.STATE;
-import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
-import static com.android.server.am.ActivityRecordProto.VISIBLE;
-import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.wm.ActivityStack.LAUNCH_TICK;
-import static com.android.server.wm.ActivityStack.LAUNCH_TICK_MSG;
-import static com.android.server.wm.ActivityStack.PAUSE_TIMEOUT_MSG;
-import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
-import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
-import static com.android.server.wm.TaskPersister.DEBUG;
-import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
+import static com.android.server.wm.TaskPersister.DEBUG;
+import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -179,9 +179,9 @@
import com.android.server.AttributeCache.Entry;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.PendingIntentRecord;
+import com.android.server.uri.UriPermissionOwner;
import com.android.server.wm.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot;
import com.android.server.wm.ActivityStack.ActivityState;
-import com.android.server.uri.UriPermissionOwner;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -303,6 +303,7 @@
// process that it is hidden.
boolean sleeping; // have we told the activity to sleep?
boolean nowVisible; // is this activity's window visible?
+ boolean mDrawn; // is this activity's window drawn?
boolean mClientVisibilityDeferred;// was the visibility change message to client deferred?
boolean idle; // has the activity gone idle?
boolean hasBeenLaunched;// has this activity ever been launched?
@@ -869,6 +870,7 @@
inHistory = false;
visible = false;
nowVisible = false;
+ mDrawn = false;
idle = false;
hasBeenLaunched = false;
mStackSupervisor = supervisor;
@@ -1944,8 +1946,12 @@
}
@Override
- public void onWindowsDrawn(long timestamp) {
+ public void onWindowsDrawn(boolean drawn, long timestamp) {
synchronized (service.mGlobalLock) {
+ mDrawn = drawn;
+ if (!drawn) {
+ return;
+ }
final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
.getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp);
final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 97eaafc..37a65cd 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -145,6 +145,7 @@
import android.hardware.display.DisplayManagerInternal;
import android.hardware.power.V1_0.PowerHint;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.FactoryTest;
@@ -835,9 +836,13 @@
}
final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
- && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE;
+ && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE
+ && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
if (!supportMultipleInstance) {
- // Can't launch home on other displays if it requested to be single instance.
+ // Can't launch home on other displays if it requested to be single instance. Also we
+ // don't allow home applications that target before Q to have multiple home activity
+ // instances because they may not be expected to have multiple home scenario and
+ // haven't explicitly request for single instance.
return false;
}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 3cbb2577..bd1460a 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -82,6 +82,7 @@
private final class H extends Handler {
public static final int NOTIFY_WINDOWS_DRAWN = 1;
public static final int NOTIFY_STARTING_WINDOW_DRAWN = 2;
+ public static final int NOTIFY_WINDOWS_NOTDRAWN = 3;
public H(Looper looper) {
super(looper);
@@ -96,16 +97,24 @@
}
if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
+ AppWindowContainerController.this.mToken);
- mListener.onWindowsDrawn(msg.getWhen());
+ mListener.onWindowsDrawn(true /* drawn */, msg.getWhen());
break;
case NOTIFY_STARTING_WINDOW_DRAWN:
if (mListener == null) {
return;
}
- if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
+ if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting starting window drawn in "
+ AppWindowContainerController.this.mToken);
mListener.onStartingWindowDrawn(msg.getWhen());
break;
+ case NOTIFY_WINDOWS_NOTDRAWN:
+ if (mListener == null) {
+ return;
+ }
+ if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting not drawn in "
+ + AppWindowContainerController.this.mToken);
+ mListener.onWindowsDrawn(false /* drawn */, msg.getWhen());
+ break;
default:
break;
}
@@ -762,6 +771,10 @@
mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_DRAWN));
}
+ void reportWindowsNotDrawn() {
+ mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_NOTDRAWN));
+ }
+
void reportWindowsVisible() {
mHandler.post(mOnWindowsVisible);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerListener.java b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
index 8a39a74..ad27669 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerListener.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
@@ -18,8 +18,8 @@
/** Interface used by the creator of the controller to listen to changes with the container. */
public interface AppWindowContainerListener extends WindowContainerListener {
- /** Called when the windows associated app window container are drawn. */
- void onWindowsDrawn(long timestamp);
+ /** Called when the windows associated app window container drawn state changes. */
+ void onWindowsDrawn(boolean drawn, long timestamp);
/** Called when the windows associated app window container are visible. */
void onWindowsVisible();
/** Called when the windows associated app window container are no longer visible. */
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 191715c..92944a0 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -366,6 +366,10 @@
if (controller != null) {
controller.reportWindowsDrawn();
}
+ } else {
+ if (controller != null) {
+ controller.reportWindowsNotDrawn();
+ }
}
reportedDrawn = nowDrawn;
}
diff --git a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
index 9cab1ed..b7d34d7 100644
--- a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
+++ b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
@@ -76,7 +76,7 @@
}
/**
- * Cleans up the session and remove itself from the service.
+ * Cleans up the session and removes it from the service.
*
* @param notifyRemoteService whether it should trigger a {@link
* IntelligenceService#onDestroyInteractionSession(InteractionSessionId)}
@@ -85,14 +85,30 @@
@GuardedBy("mLock")
public void removeSelfLocked(boolean notifyRemoteService) {
try {
- if (notifyRemoteService) {
- mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
- }
+ destroyLocked(notifyRemoteService);
} finally {
mService.removeSessionLocked(mId);
}
}
+ /**
+ * Cleans up the session, but not removes it from the service.
+ *
+ * @param notifyRemoteService whether it should trigger a {@link
+ * IntelligenceService#onDestroyInteractionSession(InteractionSessionId)}
+ * request.
+ */
+ @GuardedBy("mLock")
+ public void destroyLocked(boolean notifyRemoteService) {
+ if (mService.isVerbose()) {
+ Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")");
+ }
+ // TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
+ if (notifyRemoteService) {
+ mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
+ }
+ }
+
@Override // from RemoteScreenObservationServiceCallbacks
public void onServiceDied(AbstractRemoteService service) {
// TODO(b/111276913): implement (remove session from PerUserSession?)
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
index 43d4a44..fcfd246 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -19,6 +19,7 @@
import static android.content.Context.INTELLIGENCE_MANAGER_SERVICE;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.content.ComponentName;
import android.content.Context;
@@ -53,18 +54,20 @@
@GuardedBy("mLock")
private ActivityManagerInternal mAm;
+ private final LocalService mLocalService = new LocalService();
+
public IntelligenceManagerService(Context context) {
super(context, UserManager.DISALLOW_INTELLIGENCE_CAPTURE);
}
- @Override // from MasterSystemService
+ @Override // from AbstractMasterSystemService
protected String getServiceSettingsProperty() {
// TODO(b/111276913): STOPSHIP temporary settings, until it's set by resourcs + cmd
return "intel_service";
}
- @Override // from MasterSystemService
- protected IntelligencePerUserService newServiceLocked(int resolvedUserId,
+ @Override // from AbstractMasterSystemService
+ protected IntelligencePerUserService newServiceLocked(@UserIdInt int resolvedUserId,
boolean disabled) {
return new IntelligencePerUserService(this, mLock, resolvedUserId);
}
@@ -73,6 +76,13 @@
public void onStart() {
publishBinderService(INTELLIGENCE_MANAGER_SERVICE,
new IntelligenceManagerServiceStub());
+ publishLocalService(IntelligenceManagerInternal.class, mLocalService);
+ }
+
+ @Override // from AbstractMasterSystemService
+ protected void onServiceRemoved(@NonNull IntelligencePerUserService service,
+ @UserIdInt int userId) {
+ service.destroyLocked();
}
private ActivityManagerInternal getAmInternal() {
@@ -139,4 +149,19 @@
}
}
}
+
+ private final class LocalService extends IntelligenceManagerInternal {
+
+ @Override
+ public boolean isIntelligenceServiceForUser(int uid, int userId) {
+ synchronized (mLock) {
+ final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ return service.isIntelligenceServiceForUserLocked(uid);
+ }
+ }
+
+ return false;
+ }
+ }
}
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
index 584b872..471b40f 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -88,12 +88,18 @@
@NonNull ComponentName componentName, int taskId, int displayId,
@NonNull InteractionSessionId sessionId, int flags,
@NonNull IResultReceiver resultReceiver) {
+ if (!isEnabledLocked()) {
+ sendToClient(resultReceiver, IntelligenceManager.STATE_DISABLED);
+ return;
+ }
final ComponentName serviceComponentName = getServiceComponentName();
if (serviceComponentName == null) {
// TODO(b/111276913): this happens when the system service is starting, we should
// probably handle it in a more elegant way (like waiting for boot_complete or
// something like that
- Slog.w(TAG, "startSession(" + activityToken + "): hold your horses");
+ if (mMaster.debug) {
+ Slog.d(TAG, "startSession(" + activityToken + "): hold your horses");
+ }
return;
}
@@ -128,9 +134,15 @@
// TODO(b/111276913): log metrics
@GuardedBy("mLock")
public void finishSessionLocked(@NonNull InteractionSessionId sessionId) {
+ if (!isEnabledLocked()) {
+ return;
+ }
+
final ContentCaptureSession session = mSessions.get(sessionId);
if (session == null) {
- Slog.w(TAG, "finishSession(): no session with id" + sessionId);
+ if (mMaster.debug) {
+ Slog.d(TAG, "finishSession(): no session with id" + sessionId);
+ }
return;
}
if (mMaster.verbose) {
@@ -139,12 +151,19 @@
session.removeSelfLocked(true);
}
+ // TODO(b/111276913): need to figure out why some events are sent before session is started;
+ // probably because IntelligenceManager is not buffering them until it gets the session back
@GuardedBy("mLock")
public void sendEventsLocked(@NonNull InteractionSessionId sessionId,
@NonNull List<ContentCaptureEvent> events) {
+ if (!isEnabledLocked()) {
+ return;
+ }
final ContentCaptureSession session = mSessions.get(sessionId);
if (session == null) {
- Slog.w(TAG, "sendEvents(): no session for " + sessionId);
+ if (mMaster.verbose) {
+ Slog.v(TAG, "sendEvents(): no session for " + sessionId);
+ }
return;
}
if (mMaster.verbose) {
@@ -158,6 +177,27 @@
mSessions.remove(sessionId);
}
+ @GuardedBy("mLock")
+ public boolean isIntelligenceServiceForUserLocked(int uid) {
+ return uid == getServiceUidLocked();
+ }
+
+ /**
+ * Destroys the service and all state associated with it.
+ *
+ * <p>Called when the service was disabled (for example, if the settings change).
+ */
+ @GuardedBy("mLock")
+ public void destroyLocked() {
+ if (mMaster.debug) Slog.d(TAG, "destroyLocked()");
+ final int numSessions = mSessions.size();
+ for (int i = 0; i < numSessions; i++) {
+ final ContentCaptureSession session = mSessions.valueAt(i);
+ session.destroyLocked(true);
+ }
+ mSessions.clear();
+ }
+
@Override
protected void dumpLocked(String prefix, PrintWriter pw) {
super.dumpLocked(prefix, pw);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8aa6419..3bb8ce3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1136,6 +1136,13 @@
traceEnd();
}
+ if (!disableIntelligence) {
+ traceBeginAndSlog("StartIntelligenceService");
+ mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS);
+ traceEnd();
+ }
+
+ // NOTE: ClipboardService indirectly depends on IntelligenceService
traceBeginAndSlog("StartClipboardService");
mSystemServiceManager.startService(ClipboardService.class);
traceEnd();
@@ -1767,12 +1774,6 @@
traceEnd();
}
- if (!disableIntelligence) {
- traceBeginAndSlog("StartIntelligenceService");
- mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS);
- traceEnd();
- }
-
traceBeginAndSlog("AppServiceManager");
mSystemServiceManager.startService(AppBindingService.Lifecycle.class);
traceEnd();
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 9ab06a1..565152c 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -27,7 +27,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
services.backup \
- services.core
+ services.core \
+ services.net
include $(BUILD_PACKAGE)
diff --git a/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java b/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
deleted file mode 100644
index c162c3b..0000000
--- a/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.am;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import android.content.ContentResolver;
-import android.provider.Settings;
-import android.test.mock.MockContentResolver;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.test.FakeSettingsProvider;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Tests for {@link GlobalSettingsToPropertiesMapper}
- *
- * Build/Install/Run:
- * atest FrameworksServicesTests:GlobalSettingsToPropertiesMapperTest
- */
-@SmallTest
-public class GlobalSettingsToPropertiesMapperTest {
- private static final String[][] TEST_MAPPING = new String[][] {
- {Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "TestProperty"}
- };
-
- private TestMapper mTestMapper;
- private MockContentResolver mMockContentResolver;
-
- @Before
- public void setup() {
- // Use FakeSettingsProvider to not affect global state
- mMockContentResolver = new MockContentResolver(getInstrumentation().getTargetContext());
- mMockContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- mTestMapper = new TestMapper(mMockContentResolver);
- }
-
- @Test
- public void testUpdatePropertiesFromGlobalSettings() {
- Settings.Global.putString(mMockContentResolver,
- Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue");
-
- mTestMapper.updatePropertiesFromGlobalSettings();
- String propValue = mTestMapper.systemPropertiesGet("TestProperty");
- assertEquals("testValue", propValue);
-
- Settings.Global.putString(mMockContentResolver,
- Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
- mTestMapper.updatePropertyFromSetting(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
- "TestProperty");
- propValue = mTestMapper.systemPropertiesGet("TestProperty");
- assertEquals("testValue2", propValue);
-
- Settings.Global.putString(mMockContentResolver,
- Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
- mTestMapper.updatePropertyFromSetting(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
- "TestProperty");
- propValue = mTestMapper.systemPropertiesGet("TestProperty");
- assertEquals("", propValue);
- }
-
- @Test
- public void testUpdatePropertiesFromGlobalSettings_PropertyAndSettingNotPresent() {
- // Test that empty property will not not be set if setting is not set
- mTestMapper.updatePropertiesFromGlobalSettings();
- String propValue = mTestMapper.systemPropertiesGet("TestProperty");
- assertNull("Property should not be set if setting is null", propValue);
- }
-
- private static class TestMapper extends GlobalSettingsToPropertiesMapper {
- private final Map<String, String> mProps = new HashMap<>();
-
- TestMapper(ContentResolver contentResolver) {
- super(contentResolver, TEST_MAPPING);
- }
-
- @Override
- protected String systemPropertiesGet(String key) {
- Preconditions.checkNotNull(key);
- return mProps.get(key);
- }
-
- @Override
- protected void systemPropertiesSet(String key, String value) {
- Preconditions.checkNotNull(value);
- Preconditions.checkNotNull(key);
- mProps.put(key, value);
- }
- }
-}
-
diff --git a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
new file mode 100644
index 0000000..d965f8a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
@@ -0,0 +1,211 @@
+/*
+ * 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.am;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.text.TextUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.test.FakeSettingsProvider;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tests for {@link SettingsToPropertiesMapper}
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SettingsToPropertiesMapperTest {
+ private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\.\\-@:]*$";
+ private static final String[] TEST_MAPPING = new String[] {
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS
+ };
+
+ private TestMapper mTestMapper;
+ private MockContentResolver mMockContentResolver;
+
+ @Before
+ public void setupForEach() {
+ // Use FakeSettingsProvider to not affect global state
+ mMockContentResolver = new MockContentResolver(InstrumentationRegistry.getContext());
+ mMockContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ mTestMapper = new TestMapper(mMockContentResolver);
+ }
+
+ @Test
+ public void validateRegisteredGlobalSettings() {
+ HashSet<String> hashSet = new HashSet<>();
+ for (String globalSetting : SettingsToPropertiesMapper.sGlobalSettings) {
+ if (hashSet.contains(globalSetting)) {
+ Assert.fail("globalSetting "
+ + globalSetting
+ + " is registered more than once in "
+ + "SettingsToPropertiesMapper.sGlobalSettings.");
+ }
+ hashSet.add(globalSetting);
+ if (TextUtils.isEmpty(globalSetting)) {
+ Assert.fail("empty globalSetting registered.");
+ }
+ if (!globalSetting.matches(NAME_VALID_CHARACTERS_REGEX)) {
+ Assert.fail(globalSetting + " contains invalid characters. "
+ + "Only alphanumeric characters, '.', '-', '@', ':' and '_' are valid.");
+ }
+ }
+ }
+
+ @Test
+ public void testUpdatePropertiesFromSettings() {
+ Settings.Global.putString(mMockContentResolver,
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue");
+
+ String systemPropertyName = "persist.device_config.global_settings."
+ + "sqlite_compatibility_wal_flags";
+
+ mTestMapper.updatePropertiesFromSettings();
+ String propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+ Assert.assertEquals("testValue", propValue);
+
+ Settings.Global.putString(mMockContentResolver,
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
+ mTestMapper.updatePropertyFromSetting(
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
+ systemPropertyName,
+ true);
+ propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+ Assert.assertEquals("testValue2", propValue);
+
+ Settings.Global.putString(mMockContentResolver,
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
+ mTestMapper.updatePropertyFromSetting(
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
+ systemPropertyName,
+ true);
+ propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+ Assert.assertEquals("", propValue);
+ }
+
+ @Test
+ public void testMakePropertyName() {
+ try {
+ Assert.assertEquals("persist.device_config.test_category.test_flag",
+ SettingsToPropertiesMapper.makePropertyName("test_category", "test_flag"));
+ } catch (Exception e) {
+ Assert.fail("Unexpected exception: " + e.getMessage());
+ }
+
+ try {
+ Assert.assertEquals(null,
+ SettingsToPropertiesMapper.makePropertyName("test_category!!!", "test_flag"));
+ } catch (Exception e) {
+ Assert.fail("Unexpected exception: " + e.getMessage());
+ }
+
+ try {
+ Assert.assertEquals(null,
+ SettingsToPropertiesMapper.makePropertyName("test_category", ".test_flag"));
+ } catch (Exception e) {
+ Assert.fail("Unexpected exception: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testUpdatePropertiesFromSettings_PropertyAndSettingNotPresent() {
+ // Test that empty property will not not be set if setting is not set
+ mTestMapper.updatePropertiesFromSettings();
+ String propValue = mTestMapper.systemPropertiesGet("TestProperty");
+ Assert.assertNull("Property should not be set if setting is null", propValue);
+ }
+
+ @Test
+ public void testIsNativeFlagsResetPerformed() {
+ mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
+ Assert.assertTrue(mTestMapper.isNativeFlagsResetPerformed());
+
+ mTestMapper.systemPropertiesSet("device_config.reset_performed", "false");
+ Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed());
+
+ mTestMapper.systemPropertiesSet("device_config.reset_performed", "");
+ Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed());
+ }
+
+ @Test
+ public void testGetResetNativeCategories() {
+ mTestMapper.systemPropertiesSet("device_config.reset_performed", "");
+ Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0);
+
+ mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
+ mTestMapper.setFileContent("");
+ Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0);
+
+ mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
+ mTestMapper.setFileContent("persist.device_config.category1.flag;"
+ + "persist.device_config.category2.flag;persist.device_config.category3.flag;"
+ + "persist.device_config.category3.flag2");
+ List<String> categories = Arrays.asList(mTestMapper.getResetNativeCategories());
+ Assert.assertEquals(3, categories.size());
+ Assert.assertTrue(categories.contains("category1"));
+ Assert.assertTrue(categories.contains("category2"));
+ Assert.assertTrue(categories.contains("category3"));
+ }
+
+ private static class TestMapper extends SettingsToPropertiesMapper {
+ private final Map<String, String> mProps = new HashMap<>();
+
+ private String mFileContent = "";
+
+ TestMapper(ContentResolver contentResolver) {
+ super(contentResolver, TEST_MAPPING, new String[] {});
+ }
+
+ @Override
+ protected String systemPropertiesGet(String key) {
+ Preconditions.checkNotNull(key);
+ return mProps.get(key);
+ }
+
+ @Override
+ protected void systemPropertiesSet(String key, String value) {
+ Preconditions.checkNotNull(value);
+ Preconditions.checkNotNull(key);
+ mProps.put(key, value);
+ }
+
+ protected void setFileContent(String fileContent) {
+ mFileContent = fileContent;
+ }
+
+ @Override
+ protected String getResetFlagsFileContent() {
+ return mFileContent;
+ }
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 3fe381b..1a218b2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -27,7 +27,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -98,7 +97,7 @@
private static final UserHandle USER = UserHandle.of(0);
private static final int UID_O = 1111;
private static final String SYSTEM_PKG = "android";
- private static final int SYSTEM_UID= 1000;
+ private static final int SYSTEM_UID = 1000;
private static final UserHandle USER2 = UserHandle.of(10);
private static final String TEST_CHANNEL_ID = "test_channel_id";
private static final String TEST_AUTHORITY = "test";
@@ -1091,6 +1090,158 @@
}
@Test
+ public void testGetChannelsBypassingDndCount_noChannelsBypassing() throws Exception {
+ assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ USER.getIdentifier()).getList().size());
+ }
+
+ @Test
+ public void testGetChannelsBypassingDnd_noChannelsForUserIdBypassing()
+ throws Exception {
+ int user = 9;
+ NotificationChannel channel = new NotificationChannel("id", "name",
+ NotificationManager.IMPORTANCE_MAX);
+ channel.setBypassDnd(true);
+ mHelper.createNotificationChannel(PKG_N_MR1, 111, channel, true, true);
+
+ assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+ }
+
+ @Test
+ public void testGetChannelsBypassingDndCount_oneChannelBypassing_groupBlocked() {
+ int user = USER.getIdentifier();
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel1.setGroup(ncg.getId());
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ true);
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+
+ assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+
+ // disable group
+ ncg.setBlocked(true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ false);
+ assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+ }
+
+ @Test
+ public void testGetChannelsBypassingDndCount_multipleChannelsBypassing() {
+ int user = USER.getIdentifier();
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel2 = new NotificationChannel("id2", "name2",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel3 = new NotificationChannel("id3", "name3",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel2.setBypassDnd(true);
+ channel3.setBypassDnd(true);
+ // has DND access, so can set bypassDnd attribute
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel2, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel3, true, true);
+ assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+
+ // block notifications from this app
+ mHelper.setEnabled(PKG_N_MR1, user, false);
+ assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+
+ // re-enable notifications from this app
+ mHelper.setEnabled(PKG_N_MR1, user, true);
+ assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+
+ // setBypassDnd false for some channels
+ channel1.setBypassDnd(false);
+ channel2.setBypassDnd(false);
+ assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+
+ // setBypassDnd false for rest of the channels
+ channel3.setBypassDnd(false);
+ assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+ user).getList().size());
+ }
+
+ @Test
+ public void testGetAppsBypassingDndCount_noAppsBypassing() throws Exception {
+ assertEquals(0, mHelper.getAppsBypassingDndCount(USER.getIdentifier()));
+ }
+
+ @Test
+ public void testGetAppsBypassingDndCount_noAppsForUserIdBypassing() throws Exception {
+ int user = 9;
+ NotificationChannel channel = new NotificationChannel("id", "name",
+ NotificationManager.IMPORTANCE_MAX);
+ channel.setBypassDnd(true);
+ mHelper.createNotificationChannel(PKG_N_MR1, 111, channel, true, true);
+
+ assertEquals(0, mHelper.getAppsBypassingDndCount(user));
+ }
+
+ @Test
+ public void testGetAppsBypassingDndCount_oneChannelBypassing_groupBlocked() {
+ int user = USER.getIdentifier();
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel1.setGroup(ncg.getId());
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ true);
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+
+ assertEquals(1, mHelper.getAppsBypassingDndCount(user));
+
+ // disable group
+ ncg.setBlocked(true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ false);
+ assertEquals(0, mHelper.getAppsBypassingDndCount(user));
+ }
+
+ @Test
+ public void testGetAppsBypassingDndCount_oneAppBypassing() {
+ int user = USER.getIdentifier();
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel2 = new NotificationChannel("id2", "name2",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel3 = new NotificationChannel("id3", "name3",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel2.setBypassDnd(true);
+ channel3.setBypassDnd(true);
+ // has DND access, so can set bypassDnd attribute
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel2, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, user, channel3, true, true);
+ assertEquals(1, mHelper.getAppsBypassingDndCount(user));
+
+ // block notifications from this app
+ mHelper.setEnabled(PKG_N_MR1, user, false);
+ assertEquals(0, mHelper.getAppsBypassingDndCount(user)); // no apps can bypass dnd
+
+ // re-enable notifications from this app
+ mHelper.setEnabled(PKG_N_MR1, user, true);
+ assertEquals(1, mHelper.getAppsBypassingDndCount(user));
+
+ // setBypassDnd false for some channels
+ channel1.setBypassDnd(false);
+ channel2.setBypassDnd(false);
+ assertEquals(1, mHelper.getAppsBypassingDndCount(user));
+
+ // setBypassDnd false for rest of the channels
+ channel3.setBypassDnd(false);
+ assertEquals(0, mHelper.getAppsBypassingDndCount(user));
+ }
+
+ @Test
public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception {
// create notification channel that can't bypass dnd
// expected result: areChannelsBypassingDnd = false
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index f692a57..16dd92f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -25,6 +25,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
@@ -57,6 +58,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.graphics.Rect;
+import android.os.Build;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.MediumTest;
@@ -433,6 +435,26 @@
eq(activity), eq(null /* targetOptions */));
}
+ /**
+ * Tests home activities that targeted sdk before Q cannot start on secondary display.
+ */
+ @Test
+ public void testStartHomeTargetSdkBeforeQ() throws Exception {
+ final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+ mSupervisor.addChild(secondDisplay, POSITION_TOP);
+ doReturn(true).when(secondDisplay).supportsSystemDecorations();
+
+ final ActivityInfo info = new ActivityInfo();
+ info.launchMode = LAUNCH_MULTIPLE;
+ info.applicationInfo = new ApplicationInfo();
+ info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
+ assertTrue(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
+ false /* allowInstrumenting */));
+
+ info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
+ assertFalse(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
+ false /* allowInstrumenting */));
+ }
/**
* Tests that home activities can be started on the displays that supports system decorations.
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 33df6f9..906d64c 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -49,18 +49,27 @@
case Instruction::Op::kReturn:
out << "kReturn";
return out;
+ case Instruction::Op::kReturnObject:
+ out << "kReturnObject";
+ return out;
case Instruction::Op::kMove:
out << "kMove";
return out;
case Instruction::Op::kInvokeVirtual:
out << "kInvokeVirtual";
return out;
+ case Instruction::Op::kInvokeDirect:
+ out << "kInvokeDirect";
+ return out;
case Instruction::Op::kBindLabel:
out << "kBindLabel";
return out;
case Instruction::Op::kBranchEqz:
out << "kBranchEqz";
return out;
+ case Instruction::Op::kNew:
+ out << "kNew";
+ return out;
}
}
@@ -137,6 +146,9 @@
entry = Alloc<ir::String>();
// +1 for null terminator
entry->data = slicer::MemView{buffer.get(), header_length + string.size() + 1};
+ ::dex::u4 const new_index = dex_file_->strings_indexes.AllocateIndex();
+ dex_file_->strings_map[new_index] = entry;
+ entry->orig_index = new_index;
string_data_.push_back(std::move(buffer));
}
return entry;
@@ -161,6 +173,8 @@
ir::Type* type = Alloc<ir::Type>();
type->descriptor = GetOrAddString(descriptor);
types_by_descriptor_[descriptor] = type;
+ type->orig_index = dex_file_->types_indexes.AllocateIndex();
+ dex_file_->types_map[type->orig_index] = type;
return type;
}
@@ -217,9 +231,10 @@
decl_->prototype->param_types != nullptr ? decl_->prototype->param_types->types.size() : 0;
code->registers = num_registers_ + num_args;
code->ins_count = num_args;
- code->outs_count = decl_->prototype->return_type == dex_->GetOrAddType("V") ? 0 : 1;
EncodeInstructions();
code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size());
+ size_t const return_count = decl_->prototype->return_type == dex_->GetOrAddType("V") ? 0 : 1;
+ code->outs_count = std::max(return_count, max_args_);
method->code = code;
class_->direct_methods.push_back(method);
@@ -240,8 +255,9 @@
void MethodBuilder::BuildReturn() { AddInstruction(Instruction::OpNoArgs(Op::kReturn)); }
-void MethodBuilder::BuildReturn(Value src) {
- AddInstruction(Instruction::OpWithArgs(Op::kReturn, /*destination=*/{}, src));
+void MethodBuilder::BuildReturn(Value src, bool is_object) {
+ AddInstruction(Instruction::OpWithArgs(
+ is_object ? Op::kReturnObject : Op::kReturn, /*destination=*/{}, src));
}
void MethodBuilder::BuildConst4(Value target, int value) {
@@ -249,6 +265,11 @@
AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::Immediate(value)));
}
+void MethodBuilder::BuildConstString(Value target, const std::string& value) {
+ const ir::String* const dex_string = dex_->GetOrAddString(value);
+ AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::String(dex_string->orig_index)));
+}
+
void MethodBuilder::EncodeInstructions() {
buffer_.clear();
for (const auto& instruction : instructions_) {
@@ -259,27 +280,32 @@
void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
switch (instruction.opcode()) {
case Instruction::Op::kReturn:
- return EncodeReturn(instruction);
+ return EncodeReturn(instruction, ::art::Instruction::RETURN);
+ case Instruction::Op::kReturnObject:
+ return EncodeReturn(instruction, ::art::Instruction::RETURN_OBJECT);
case Instruction::Op::kMove:
return EncodeMove(instruction);
case Instruction::Op::kInvokeVirtual:
- return EncodeInvokeVirtual(instruction);
+ return EncodeInvoke(instruction, art::Instruction::INVOKE_VIRTUAL);
+ case Instruction::Op::kInvokeDirect:
+ return EncodeInvoke(instruction, art::Instruction::INVOKE_DIRECT);
case Instruction::Op::kBindLabel:
return BindLabel(instruction.args()[0]);
case Instruction::Op::kBranchEqz:
return EncodeBranch(art::Instruction::IF_EQZ, instruction);
+ case Instruction::Op::kNew:
+ return EncodeNew(instruction);
}
}
-void MethodBuilder::EncodeReturn(const Instruction& instruction) {
- DCHECK_EQ(Instruction::Op::kReturn, instruction.opcode());
+void MethodBuilder::EncodeReturn(const Instruction& instruction, ::art::Instruction::Code opcode) {
DCHECK(!instruction.dest().has_value());
if (instruction.args().size() == 0) {
- buffer_.push_back(art::Instruction::RETURN_VOID);
+ Encode10x(art::Instruction::RETURN_VOID);
} else {
- DCHECK(instruction.args().size() == 1);
+ DCHECK_EQ(1, instruction.args().size());
size_t source = RegisterValue(instruction.args()[0]);
- buffer_.push_back(art::Instruction::RETURN | source << 8);
+ Encode11x(opcode, source);
}
}
@@ -294,31 +320,43 @@
if (source.is_immediate()) {
// TODO: support more registers
DCHECK_LT(RegisterValue(*instruction.dest()), 16);
- DCHECK_LT(source.value(), 16);
- buffer_.push_back(art::Instruction::CONST_4 | (source.value() << 12) |
- (RegisterValue(*instruction.dest()) << 8));
+ Encode11n(art::Instruction::CONST_4, RegisterValue(*instruction.dest()), source.value());
+ } else if (source.is_string()) {
+ constexpr size_t kMaxRegisters = 256;
+ DCHECK_LT(RegisterValue(*instruction.dest()), kMaxRegisters);
+ DCHECK_LT(source.value(), 65536); // make sure we don't need a jumbo string
+ Encode21c(::art::Instruction::CONST_STRING, RegisterValue(*instruction.dest()), source.value());
} else {
UNIMPLEMENTED(FATAL);
}
}
-void MethodBuilder::EncodeInvokeVirtual(const Instruction& instruction) {
- DCHECK_EQ(Instruction::Op::kInvokeVirtual, instruction.opcode());
+void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode) {
+ constexpr size_t kMaxArgs = 5;
- // TODO: support more than one argument (i.e. the this argument) and change this to DCHECK_GE
- DCHECK_EQ(1, instruction.args().size());
+ CHECK_LE(instruction.args().size(), kMaxArgs);
- const Value& this_arg = instruction.args()[0];
-
- size_t real_reg = RegisterValue(this_arg) & 0xf;
- buffer_.push_back(1 << 12 | art::Instruction::INVOKE_VIRTUAL);
- buffer_.push_back(instruction.method_id());
- buffer_.push_back(real_reg);
-
- if (instruction.dest().has_value()) {
- real_reg = RegisterValue(*instruction.dest());
- buffer_.push_back(real_reg << 8 | art::Instruction::MOVE_RESULT);
+ uint8_t arguments[kMaxArgs]{};
+ for (size_t i = 0; i < instruction.args().size(); ++i) {
+ CHECK(instruction.args()[i].is_variable());
+ arguments[i] = RegisterValue(instruction.args()[i]);
}
+
+ Encode35c(opcode,
+ instruction.args().size(),
+ instruction.method_id(),
+ arguments[0],
+ arguments[1],
+ arguments[2],
+ arguments[3],
+ arguments[4]);
+
+ // If there is a return value, add a move-result instruction
+ if (instruction.dest().has_value()) {
+ Encode11x(art::Instruction::MOVE_RESULT, RegisterValue(*instruction.dest()));
+ }
+
+ max_args_ = std::max(max_args_, instruction.args().size());
}
// Encodes a conditional branch that tests a single argument.
@@ -331,9 +369,21 @@
CHECK(branch_target.is_label());
size_t instruction_offset = buffer_.size();
- buffer_.push_back(op | (RegisterValue(test_value) << 8));
- size_t field_offset = buffer_.size();
- buffer_.push_back(LabelValue(branch_target, instruction_offset, field_offset));
+ size_t field_offset = buffer_.size() + 1;
+ Encode21c(
+ op, RegisterValue(test_value), LabelValue(branch_target, instruction_offset, field_offset));
+}
+
+void MethodBuilder::EncodeNew(const Instruction& instruction) {
+ DCHECK_EQ(Instruction::Op::kNew, instruction.opcode());
+ DCHECK(instruction.dest().has_value());
+ DCHECK(instruction.dest()->is_variable());
+ DCHECK_EQ(1, instruction.args().size());
+
+ const Value& type = instruction.args()[0];
+ DCHECK_LT(RegisterValue(*instruction.dest()), 256);
+ DCHECK(type.is_type());
+ Encode21c(::art::Instruction::NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value());
}
size_t MethodBuilder::RegisterValue(const Value& value) const {
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 0744151..adf82bf 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -110,18 +110,22 @@
static constexpr Value Local(size_t id) { return Value{id, Kind::kLocalRegister}; }
static constexpr Value Parameter(size_t id) { return Value{id, Kind::kParameter}; }
static constexpr Value Immediate(size_t value) { return Value{value, Kind::kImmediate}; }
+ static constexpr Value String(size_t value) { return Value{value, Kind::kString}; }
static constexpr Value Label(size_t id) { return Value{id, Kind::kLabel}; }
+ static constexpr Value Type(size_t id) { return Value{id, Kind::kType}; }
bool is_register() const { return kind_ == Kind::kLocalRegister; }
bool is_parameter() const { return kind_ == Kind::kParameter; }
bool is_variable() const { return is_register() || is_parameter(); }
bool is_immediate() const { return kind_ == Kind::kImmediate; }
+ bool is_string() const { return kind_ == Kind::kString; }
bool is_label() const { return kind_ == Kind::kLabel; }
+ bool is_type() const { return kind_ == Kind::kType; }
size_t value() const { return value_; }
private:
- enum class Kind { kLocalRegister, kParameter, kImmediate, kLabel };
+ enum class Kind { kLocalRegister, kParameter, kImmediate, kString, kLabel, kType };
const size_t value_;
const Kind kind_;
@@ -137,7 +141,16 @@
public:
// The operation performed by this instruction. These are virtual instructions that do not
// correspond exactly to DEX instructions.
- enum class Op { kReturn, kMove, kInvokeVirtual, kBindLabel, kBranchEqz };
+ enum class Op {
+ kReturn,
+ kReturnObject,
+ kMove,
+ kInvokeVirtual,
+ kInvokeDirect,
+ kBindLabel,
+ kBranchEqz,
+ kNew
+ };
////////////////////////
// Named Constructors //
@@ -158,6 +171,12 @@
Value this_arg, T... args) {
return Instruction{Op::kInvokeVirtual, method_id, dest, this_arg, args...};
}
+ // For direct calls (basically, constructors).
+ template <typename... T>
+ static inline Instruction InvokeDirect(size_t method_id, std::optional<const Value> dest,
+ Value this_arg, T... args) {
+ return Instruction{Op::kInvokeDirect, method_id, dest, this_arg, args...};
+ }
///////////////
// Accessors //
@@ -187,6 +206,12 @@
// Needed for CHECK_EQ, DCHECK_EQ, etc.
std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode);
+// Keeps track of information needed to manipulate or call a method.
+struct MethodDeclData {
+ size_t id;
+ ir::MethodDecl* decl;
+};
+
// Tools to help build methods and their bodies.
class MethodBuilder {
public:
@@ -210,19 +235,74 @@
// return-void
void BuildReturn();
- void BuildReturn(Value src);
+ void BuildReturn(Value src, bool is_object = false);
// const/4
void BuildConst4(Value target, int value);
+ void BuildConstString(Value target, const std::string& value);
+ template <typename... T>
+ void BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args);
// TODO: add builders for more instructions
private:
void EncodeInstructions();
void EncodeInstruction(const Instruction& instruction);
- void EncodeReturn(const Instruction& instruction);
+
+ // Encodes a return instruction. For instructions with no return value, the opcode field is
+ // ignored. Otherwise, this specifies which return instruction will be used (return,
+ // return-object, etc.)
+ void EncodeReturn(const Instruction& instruction, ::art::Instruction::Code opcode);
+
void EncodeMove(const Instruction& instruction);
- void EncodeInvokeVirtual(const Instruction& instruction);
+ void EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode);
void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
+ void EncodeNew(const Instruction& instruction);
+
+ // Low-level instruction format encoding. See
+ // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
+ // formats.
+
+ inline void Encode10x(art::Instruction::Code opcode) {
+ // 00|op
+ buffer_.push_back(opcode);
+ }
+
+ inline void Encode11x(art::Instruction::Code opcode, uint8_t a) {
+ // aa|op
+ buffer_.push_back((a << 8) | opcode);
+ }
+
+ inline void Encode11n(art::Instruction::Code opcode, uint8_t a, int8_t b) {
+ // b|a|op
+
+ // Make sure the fields are in bounds (4 bits for a, 4 bits for b).
+ CHECK_LT(a, 16);
+ CHECK_LE(-8, b);
+ CHECK_LT(b, 8);
+
+ buffer_.push_back(((b & 0xf) << 12) | (a << 8) | opcode);
+ }
+
+ inline void Encode21c(art::Instruction::Code opcode, uint8_t a, uint16_t b) {
+ // aa|op|bbbb
+ buffer_.push_back((a << 8) | opcode);
+ buffer_.push_back(b);
+ }
+
+ inline void Encode35c(art::Instruction::Code opcode, size_t a, uint16_t b, uint8_t c, uint8_t d,
+ uint8_t e, uint8_t f, uint8_t g) {
+ // a|g|op|bbbb|f|e|d|c
+
+ CHECK_LE(a, 5);
+ CHECK_LT(c, 16);
+ CHECK_LT(d, 16);
+ CHECK_LT(e, 16);
+ CHECK_LT(f, 16);
+ CHECK_LT(g, 16);
+ buffer_.push_back((a << 12) | (g << 8) | opcode);
+ buffer_.push_back(b);
+ buffer_.push_back((f << 12) | (e << 8) | (d << 4) | c);
+ }
// Converts a register or parameter to its DEX register number.
size_t RegisterValue(const Value& value) const;
@@ -262,6 +342,10 @@
};
std::vector<LabelData> labels_;
+
+ // During encoding, keep track of the largest number of arguments needed, so we can use it for our
+ // outs count
+ size_t max_args_{0};
};
// A helper to build class definitions.
@@ -281,12 +365,6 @@
ir::Class* const class_;
};
-// Keeps track of information needed to manipulate or call a method.
-struct MethodDeclData {
- size_t id;
- ir::MethodDecl* decl;
-};
-
// Builds Dex files from scratch.
class DexBuilder {
public:
@@ -355,6 +433,17 @@
std::map<Prototype, ir::Proto*> proto_map_;
};
+template <typename... T>
+void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args) {
+ MethodDeclData constructor_data{dex_->GetOrDeclareMethod(type, "<init>", constructor)};
+ // allocate the object
+ ir::Type* type_def = dex_->GetOrAddType(type.descriptor());
+ AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kNew, target, Value::Type(type_def->orig_index)));
+ // call the constructor
+ AddInstruction(Instruction::InvokeDirect(constructor_data.id, /*dest=*/{}, target, args...));
+};
+
} // namespace dex
} // namespace startop
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index 169c633..e20f3a9 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -50,6 +50,14 @@
}
@Test
+ public void returnInteger5() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("returnInteger5");
+ Assert.assertEquals(5, method.invoke(null));
+ }
+
+ @Test
public void returnParam() throws Exception {
ClassLoader loader = loadDexFile("simple.dex");
Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
@@ -82,4 +90,38 @@
Method method = clazz.getMethod("backwardsBranch");
Assert.assertEquals(2, method.invoke(null));
}
+
+ @Test
+ public void returnNull() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("returnNull");
+ Assert.assertEquals(null, method.invoke(null));
+ }
+
+ @Test
+ public void makeString() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("makeString");
+ Assert.assertEquals("Hello, World!", method.invoke(null));
+ }
+
+ @Test
+ public void returnStringIfZeroAB() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("returnStringIfZeroAB", int.class);
+ Assert.assertEquals("a", method.invoke(null, 0));
+ Assert.assertEquals("b", method.invoke(null, 1));
+ }
+
+ @Test
+ public void returnStringIfZeroBA() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("returnStringIfZeroBA", int.class);
+ Assert.assertEquals("b", method.invoke(null, 0));
+ Assert.assertEquals("a", method.invoke(null, 1));
+ }
}
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index c521bf2..e2bf43bc 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -53,6 +53,19 @@
}
return5.Encode();
+ // int return5() { return 5; }
+ auto integer_type{TypeDescriptor::FromClassname("java.lang.Integer")};
+ auto returnInteger5{cbuilder.CreateMethod("returnInteger5", Prototype{integer_type})};
+ [&](MethodBuilder& method) {
+ Value five{method.MakeRegister()};
+ method.BuildConst4(five, 5);
+ Value object{method.MakeRegister()};
+ method.BuildNew(
+ object, integer_type, Prototype{TypeDescriptor::Void(), TypeDescriptor::Int()}, five);
+ method.BuildReturn(object, /*is_object=*/true);
+ }(returnInteger5);
+ returnInteger5.Encode();
+
// // int returnParam(int x) { return x; }
auto returnParam{cbuilder.CreateMethod("returnParam",
Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
@@ -138,6 +151,71 @@
}(backwardsBranch);
backwardsBranch.Encode();
+ // Test that we can make a null value. Basically:
+ //
+ // public static String returnNull() { return null; }
+ MethodBuilder returnNull{cbuilder.CreateMethod("returnNull", Prototype{string_type})};
+ [](MethodBuilder& method) {
+ Value zero = method.MakeRegister();
+ method.BuildConst4(zero, 0);
+ method.BuildReturn(zero, /*is_object=*/true);
+ }(returnNull);
+ returnNull.Encode();
+
+ // Test that we can make String literals. Basically:
+ //
+ // public static String makeString() { return "Hello, World!"; }
+ MethodBuilder makeString{cbuilder.CreateMethod("makeString", Prototype{string_type})};
+ [](MethodBuilder& method) {
+ Value string = method.MakeRegister();
+ method.BuildConstString(string, "Hello, World!");
+ method.BuildReturn(string, /*is_object=*/true);
+ }(makeString);
+ makeString.Encode();
+
+ // Make sure strings are sorted correctly.
+ //
+ // int returnStringIfZeroAB(int x) { if (x == 0) { return "a"; } else { return "b"; } }
+ MethodBuilder returnStringIfZeroAB{
+ cbuilder.CreateMethod("returnStringIfZeroAB", Prototype{string_type, TypeDescriptor::Int()})};
+ [&](MethodBuilder& method) {
+ Value resultIfZero{method.MakeRegister()};
+ Value else_target{method.MakeLabel()};
+ method.AddInstruction(Instruction::OpWithArgs(
+ Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
+ // else branch
+ method.BuildConstString(resultIfZero, "b");
+ method.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
+ // then branch
+ method.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
+ method.BuildConstString(resultIfZero, "a");
+ method.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
+ method.Encode();
+ }(returnStringIfZeroAB);
+ // int returnStringIfZeroAB(int x) { if (x == 0) { return "b"; } else { return "a"; } }
+ MethodBuilder returnStringIfZeroBA{
+ cbuilder.CreateMethod("returnStringIfZeroBA", Prototype{string_type, TypeDescriptor::Int()})};
+ [&](MethodBuilder& method) {
+ Value resultIfZero{method.MakeRegister()};
+ Value else_target{method.MakeLabel()};
+ method.AddInstruction(Instruction::OpWithArgs(
+ Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
+ // else branch
+ method.BuildConstString(resultIfZero, "a");
+ method.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
+ // then branch
+ method.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
+ method.BuildConstString(resultIfZero, "b");
+ method.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
+ method.Encode();
+ }(returnStringIfZeroBA);
+
slicer::MemView image{dex_file.CreateImage()};
std::ofstream out_file(outdir + "/simple.dex");
out_file.write(image.ptr<const char>(), image.size());
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fbc54ae6..b744770 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1320,18 +1320,13 @@
public static final String KEY_MMS_CLOSE_CONNECTION_BOOL = "mmsCloseConnection";
/**
- * If carriers require differentiate un-provisioned status: cold sim or out of credit sim
- * a package name and activity name can be provided to launch a supported carrier application
- * that check the sim provisioning status
- * The first element is the package name and the second element is the activity name
- * of the provisioning app
- * example:
- * <item>com.google.android.carrierPackageName</item>
- * <item>com.google.android.carrierPackageName.CarrierActivityName</item>
- * The ComponentName of the carrier activity that can setup the device and activate with the
- * network as part of the Setup Wizard flow.
+ * The flatten {@link android.content.ComponentName componentName} of the activity that can
+ * setup the device and activate with the network per carrier requirements.
+ *
+ * e.g, com.google.android.carrierPackageName/.CarrierActivityName
* @hide
*/
+ @SystemApi
public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
/**
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index f5dff20..3c5ad84 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -21,16 +21,18 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Looper;
-import android.os.Message;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IPhoneStateListener;
import java.lang.ref.WeakReference;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* A listener class for monitoring changes in specific telephony states
@@ -231,34 +233,35 @@
public static final int LISTEN_CARRIER_NETWORK_CHANGE = 0x00010000;
/**
- * Listen for changes to the sim voice activation state
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
- * {@more}
- * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
- * fully activated
+ * Listen for changes to the sim voice activation state
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+ * {@more}
+ * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
+ * fully activated
*
- * @see #onVoiceActivationStateChanged
- * @hide
+ * @see #onVoiceActivationStateChanged
+ * @hide
*/
+ @SystemApi
public static final int LISTEN_VOICE_ACTIVATION_STATE = 0x00020000;
/**
- * Listen for changes to the sim data activation state
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
- * {@more}
- * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
- * fully activated
+ * Listen for changes to the sim data activation state
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+ * {@more}
+ * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
+ * fully activated
*
- * @see #onDataActivationStateChanged
- * @hide
+ * @see #onDataActivationStateChanged
+ * @hide
*/
public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000;
@@ -320,7 +323,12 @@
@UnsupportedAppUsage
protected Integer mSubId;
- private final Handler mHandler;
+ /**
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @UnsupportedAppUsage
+ public final IPhoneStateListener callback;
/**
* Create a PhoneStateListener for the Phone with the default subscription.
@@ -358,95 +366,27 @@
*/
@UnsupportedAppUsage
public PhoneStateListener(Integer subId, Looper looper) {
- if (DBG) log("ctor: subId=" + subId + " looper=" + looper);
+ this(subId, new HandlerExecutor(new Handler(looper)));
+ }
+
+ /**
+ * Create a PhoneStateListener for the Phone using the specified Executor
+ *
+ * <p>Create a PhoneStateListener with a specified Executor for handling necessary callbacks.
+ * The Executor must not be null.
+ *
+ * @param executor a non-null Executor that will execute callbacks for the PhoneStateListener.
+ */
+ public PhoneStateListener(@NonNull Executor executor) {
+ this(null, executor);
+ }
+
+ private PhoneStateListener(Integer subId, Executor e) {
+ if (e == null) {
+ throw new IllegalArgumentException("PhoneStateListener Executor must be non-null");
+ }
mSubId = subId;
- mHandler = new Handler(looper) {
- public void handleMessage(Message msg) {
- if (DBG) {
- log("mSubId=" + mSubId + " what=0x" + Integer.toHexString(msg.what)
- + " msg=" + msg);
- }
- switch (msg.what) {
- case LISTEN_SERVICE_STATE:
- PhoneStateListener.this.onServiceStateChanged((ServiceState)msg.obj);
- break;
- case LISTEN_SIGNAL_STRENGTH:
- PhoneStateListener.this.onSignalStrengthChanged(msg.arg1);
- break;
- case LISTEN_MESSAGE_WAITING_INDICATOR:
- PhoneStateListener.this.onMessageWaitingIndicatorChanged(msg.arg1 != 0);
- break;
- case LISTEN_CALL_FORWARDING_INDICATOR:
- PhoneStateListener.this.onCallForwardingIndicatorChanged(msg.arg1 != 0);
- break;
- case LISTEN_CELL_LOCATION:
- PhoneStateListener.this.onCellLocationChanged((CellLocation)msg.obj);
- break;
- case LISTEN_CALL_STATE:
- PhoneStateListener.this.onCallStateChanged(msg.arg1, (String)msg.obj);
- break;
- case LISTEN_DATA_CONNECTION_STATE:
- PhoneStateListener.this.onDataConnectionStateChanged(msg.arg1, msg.arg2);
- PhoneStateListener.this.onDataConnectionStateChanged(msg.arg1);
- break;
- case LISTEN_DATA_ACTIVITY:
- PhoneStateListener.this.onDataActivity(msg.arg1);
- break;
- case LISTEN_SIGNAL_STRENGTHS:
- PhoneStateListener.this.onSignalStrengthsChanged((SignalStrength)msg.obj);
- break;
- case LISTEN_OTASP_CHANGED:
- PhoneStateListener.this.onOtaspChanged(msg.arg1);
- break;
- case LISTEN_CELL_INFO:
- PhoneStateListener.this.onCellInfoChanged((List<CellInfo>)msg.obj);
- break;
- case LISTEN_PRECISE_CALL_STATE:
- PhoneStateListener.this.onPreciseCallStateChanged((PreciseCallState)msg.obj);
- break;
- case LISTEN_PRECISE_DATA_CONNECTION_STATE:
- PhoneStateListener.this.onPreciseDataConnectionStateChanged(
- (PreciseDataConnectionState)msg.obj);
- break;
- case LISTEN_DATA_CONNECTION_REAL_TIME_INFO:
- PhoneStateListener.this.onDataConnectionRealTimeInfoChanged(
- (DataConnectionRealTimeInfo)msg.obj);
- break;
- case LISTEN_SRVCC_STATE_CHANGED:
- PhoneStateListener.this.onSrvccStateChanged((int) msg.obj);
- break;
- case LISTEN_VOICE_ACTIVATION_STATE:
- PhoneStateListener.this.onVoiceActivationStateChanged((int)msg.obj);
- break;
- case LISTEN_DATA_ACTIVATION_STATE:
- PhoneStateListener.this.onDataActivationStateChanged((int)msg.obj);
- break;
- case LISTEN_USER_MOBILE_DATA_STATE:
- PhoneStateListener.this.onUserMobileDataStateChanged((boolean)msg.obj);
- break;
- case LISTEN_OEM_HOOK_RAW_EVENT:
- PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj);
- break;
- case LISTEN_CARRIER_NETWORK_CHANGE:
- PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
- break;
- case LISTEN_PHYSICAL_CHANNEL_CONFIGURATION:
- PhoneStateListener.this.onPhysicalChannelConfigurationChanged(
- (List<PhysicalChannelConfig>)msg.obj);
- break;
- case LISTEN_PHONE_CAPABILITY_CHANGE:
- PhoneStateListener.this.onPhoneCapabilityChanged(
- (PhoneCapability) msg.obj);
- break;
- case LISTEN_PREFERRED_DATA_SUBID_CHANGE:
- PhoneStateListener.this.onPreferredDataSubIdChanged((int) msg.obj);
- break;
- case LISTEN_RADIO_POWER_STATE_CHANGED:
- PhoneStateListener.this.onRadioPowerStateChanged((int) msg.obj);
- break;
- }
- }
- };
+ callback = new IPhoneStateListenerStub(this, e);
}
/**
@@ -630,8 +570,8 @@
* @param state is the current SIM voice activation state
* @hide
*/
- public void onVoiceActivationStateChanged(int state) {
-
+ @SystemApi
+ public void onVoiceActivationStateChanged(@TelephonyManager.SimActivationState int state) {
}
/**
@@ -639,8 +579,7 @@
* @param state is the current SIM data activation state
* @hide
*/
- public void onDataActivationStateChanged(int state) {
-
+ public void onDataActivationStateChanged(@TelephonyManager.SimActivationState int state) {
}
/**
@@ -735,127 +674,217 @@
*/
private static class IPhoneStateListenerStub extends IPhoneStateListener.Stub {
private WeakReference<PhoneStateListener> mPhoneStateListenerWeakRef;
+ private Executor mExecutor;
- public IPhoneStateListenerStub(PhoneStateListener phoneStateListener) {
+ IPhoneStateListenerStub(PhoneStateListener phoneStateListener, Executor executor) {
mPhoneStateListenerWeakRef = new WeakReference<PhoneStateListener>(phoneStateListener);
- }
-
- private void send(int what, int arg1, int arg2, Object obj) {
- PhoneStateListener listener = mPhoneStateListenerWeakRef.get();
- if (listener != null) {
- Message.obtain(listener.mHandler, what, arg1, arg2, obj).sendToTarget();
- }
+ mExecutor = executor;
}
public void onServiceStateChanged(ServiceState serviceState) {
- send(LISTEN_SERVICE_STATE, 0, 0, serviceState);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onServiceStateChanged(serviceState)));
}
public void onSignalStrengthChanged(int asu) {
- send(LISTEN_SIGNAL_STRENGTH, asu, 0, null);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onSignalStrengthChanged(asu)));
}
public void onMessageWaitingIndicatorChanged(boolean mwi) {
- send(LISTEN_MESSAGE_WAITING_INDICATOR, mwi ? 1 : 0, 0, null);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onMessageWaitingIndicatorChanged(mwi)));
}
public void onCallForwardingIndicatorChanged(boolean cfi) {
- send(LISTEN_CALL_FORWARDING_INDICATOR, cfi ? 1 : 0, 0, null);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCallForwardingIndicatorChanged(cfi)));
}
public void onCellLocationChanged(Bundle bundle) {
CellLocation location = CellLocation.newFromBundle(bundle);
- send(LISTEN_CELL_LOCATION, 0, 0, location);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCellLocationChanged(location)));
}
public void onCallStateChanged(int state, String incomingNumber) {
- send(LISTEN_CALL_STATE, state, 0, incomingNumber);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCallStateChanged(state, incomingNumber)));
}
public void onDataConnectionStateChanged(int state, int networkType) {
- send(LISTEN_DATA_CONNECTION_STATE, state, networkType, null);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onDataConnectionStateChanged(state, networkType)));
}
public void onDataActivity(int direction) {
- send(LISTEN_DATA_ACTIVITY, direction, 0, null);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onDataActivity(direction)));
}
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
- send(LISTEN_SIGNAL_STRENGTHS, 0, 0, signalStrength);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onSignalStrengthsChanged(signalStrength)));
}
public void onOtaspChanged(int otaspMode) {
- send(LISTEN_OTASP_CHANGED, otaspMode, 0, null);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onOtaspChanged(otaspMode)));
}
public void onCellInfoChanged(List<CellInfo> cellInfo) {
- send(LISTEN_CELL_INFO, 0, 0, cellInfo);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCellInfoChanged(cellInfo)));
}
public void onPreciseCallStateChanged(PreciseCallState callState) {
- send(LISTEN_PRECISE_CALL_STATE, 0, 0, callState);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onPreciseCallStateChanged(callState)));
}
public void onPreciseDataConnectionStateChanged(
PreciseDataConnectionState dataConnectionState) {
- send(LISTEN_PRECISE_DATA_CONNECTION_STATE, 0, 0, dataConnectionState);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onPreciseDataConnectionStateChanged(dataConnectionState)));
}
- public void onDataConnectionRealTimeInfoChanged(
- DataConnectionRealTimeInfo dcRtInfo) {
- send(LISTEN_DATA_CONNECTION_REAL_TIME_INFO, 0, 0, dcRtInfo);
+ public void onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo dcRtInfo) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onDataConnectionRealTimeInfoChanged(dcRtInfo)));
}
public void onSrvccStateChanged(int state) {
- send(LISTEN_SRVCC_STATE_CHANGED, 0, 0, state);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onSrvccStateChanged(state)));
}
public void onVoiceActivationStateChanged(int activationState) {
- send(LISTEN_VOICE_ACTIVATION_STATE, 0, 0, activationState);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onVoiceActivationStateChanged(activationState)));
}
public void onDataActivationStateChanged(int activationState) {
- send(LISTEN_DATA_ACTIVATION_STATE, 0, 0, activationState);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onDataActivationStateChanged(activationState)));
}
public void onUserMobileDataStateChanged(boolean enabled) {
- send(LISTEN_USER_MOBILE_DATA_STATE, 0, 0, enabled);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onUserMobileDataStateChanged(enabled)));
}
public void onOemHookRawEvent(byte[] rawData) {
- send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onOemHookRawEvent(rawData)));
}
public void onCarrierNetworkChange(boolean active) {
- send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCarrierNetworkChange(active)));
}
public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
- send(LISTEN_PHYSICAL_CHANNEL_CONFIGURATION, 0, 0, configs);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onPhysicalChannelConfigurationChanged(configs)));
}
public void onPhoneCapabilityChanged(PhoneCapability capability) {
- send(LISTEN_PHONE_CAPABILITY_CHANGE, 0, 0, capability);
- }
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
- public void onPreferredDataSubIdChanged(int subId) {
- send(LISTEN_PREFERRED_DATA_SUBID_CHANGE, 0, 0, subId);
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onPhoneCapabilityChanged(capability)));
}
public void onRadioPowerStateChanged(@TelephonyManager.RadioPowerState int state) {
- send(LISTEN_RADIO_POWER_STATE_CHANGED, 0, 0, state);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onRadioPowerStateChanged(state)));
}
+ public void onPreferredDataSubIdChanged(int subId) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onPreferredDataSubIdChanged(subId)));
+ }
}
- /**
- * @hide
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- @UnsupportedAppUsage
- public final IPhoneStateListener callback = new IPhoneStateListenerStub(this);
private void log(String s) {
Rlog.d(LOG_TAG, s);
}
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index c9cf473..e06c372 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -292,7 +292,7 @@
* Create an instance of ImsManager for the subscription id specified.
*
* @param context
- * @param subId The ID of the subscription that this ImsManager will use.
+ * @param subId The ID of the subscription that this ImsMmTelManager will use.
* @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
* @throws IllegalArgumentException if the subscription is invalid or
* the subscription ID is not an active subscription.
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
new file mode 100644
index 0000000..916e282
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -0,0 +1,252 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsConfigCallback;
+import android.telephony.ims.stub.ImsConfigImplBase;
+
+import com.android.internal.telephony.ITelephony;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Manages IMS provisioning and configuration parameters, as well as callbacks for apps to listen
+ * to changes in these configurations.
+ *
+ * Note: IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
+ * applications and may vary.
+ * @hide
+ */
+@SystemApi
+public class ProvisioningManager {
+
+ /**
+ * Callback for IMS provisioning changes.
+ */
+ public static class Callback {
+
+ private static class CallbackBinder extends IImsConfigCallback.Stub {
+
+ private final Callback mLocalConfigurationCallback;
+ private Executor mExecutor;
+
+ private CallbackBinder(Callback localConfigurationCallback) {
+ mLocalConfigurationCallback = localConfigurationCallback;
+ }
+
+ @Override
+ public final void onIntConfigChanged(int item, int value) {
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() ->
+ mLocalConfigurationCallback.onProvisioningIntChanged(item, value)));
+ }
+
+ @Override
+ public final void onStringConfigChanged(int item, String value) {
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() ->
+ mLocalConfigurationCallback.onProvisioningStringChanged(item,
+ value)));
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+ }
+
+ private final CallbackBinder mBinder = new CallbackBinder(this);
+
+ /**
+ * Called when a provisioning item has changed.
+ * @param item the IMS provisioning key constant, as defined by the OEM.
+ * @param value the new integer value of the IMS provisioning key.
+ */
+ public void onProvisioningIntChanged(int item, int value) {
+ // Base Implementation
+ }
+
+ /**
+ * Called when a provisioning item has changed.
+ * @param item the IMS provisioning key constant, as defined by the OEM.
+ * @param value the new String value of the IMS configuration constant.
+ */
+ public void onProvisioningStringChanged(int item, String value) {
+ // Base Implementation
+ }
+
+ /**@hide*/
+ public final IImsConfigCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ public void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ private int mSubId;
+
+ /**
+ * Create a new {@link ProvisioningManager} for the subscription specified.
+ * @param context The context that this manager will use.
+ * @param subId The ID of the subscription that this ProvisioningManager will use.
+ * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+ * @throws IllegalArgumentException if the subscription is invalid or
+ * the subscription ID is not an active subscription.
+ */
+ public static ProvisioningManager createForSubscriptionId(Context context, int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)
+ || !getSubscriptionManager(context).isActiveSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
+ return new ProvisioningManager(subId);
+ }
+
+ private ProvisioningManager(int subId) {
+ mSubId = subId;
+ }
+
+ /**
+ * Register a new {@link Callback} to listen to changes to changes in
+ * IMS provisioning. Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
+ * Subscription changed events and call
+ * {@link #unregisterProvisioningChangedCallback(Callback)} to clean up after a
+ * subscription is removed.
+ * @param executor The {@link Executor} to call the callback methods on
+ * @param callback The provisioning callbackto be registered.
+ * @see #unregisterProvisioningChangedCallback(Callback)
+ * @see SubscriptionManager.OnSubscriptionsChangedListener
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerProvisioningChangedCallback(@CallbackExecutor Executor executor,
+ @NonNull Callback callback) {
+ callback.setExecutor(executor);
+ try {
+ getITelephony().registerImsProvisioningChangedCallback(mSubId,
+ callback.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Unregister an existing {@link Callback}. Ensure to call this method when cleaning
+ * up to avoid memory leaks or when the subscription is removed.
+ * @param callback The existing {@link Callback} to be removed.
+ * @see #registerProvisioningChangedCallback(Executor, Callback)
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterProvisioningChangedCallback(@NonNull Callback callback) {
+ try {
+ getITelephony().unregisterImsProvisioningChangedCallback(mSubId,
+ callback.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query for the integer value associated with the provided key.
+ * @param key An integer that represents the provisioning key, which is defined by the OEM.
+ * @return an integer value for the provided key.
+ * @throws IllegalArgumentException if the key provided was invalid.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public int getProvisioningIntValue(int key) {
+ try {
+ return getITelephony().getImsProvisioningInt(mSubId, key);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query for the String value associated with the provided key.
+ * @param key An integer that represents the provisioning key, which is defined by the OEM.
+ * @return a String value for the provided key, or {@code null} if the key doesn't exist.
+ * @throws IllegalArgumentException if the key provided was invalid.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public String getProvisioningStringValue(int key) {
+ try {
+ return getITelephony().getImsProvisioningString(mSubId, key);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the integer value associated with the provided key.
+ * @param key An integer that represents the provisioning key, which is defined by the OEM.
+ * @param value a integer value for the provided key.
+ * @return the result of setting the configuration value.
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public @ImsConfigImplBase.SetConfigResult int setProvisioningIntValue(int key, int value) {
+ try {
+ return getITelephony().setImsProvisioningInt(mSubId, key, value);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the String value associated with the provided key.
+ *
+ * @param key An integer that represents the provisioning key, which is defined by the OEM.
+ * @param value a String value for the provided key.
+ * @return the result of setting the configuration value.
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public @ImsConfigImplBase.SetConfigResult int setProvisioningStringValue(int key,
+ String value) {
+ try {
+ return getITelephony().setImsProvisioningString(mSubId, key, value);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ private static SubscriptionManager getSubscriptionManager(Context context) {
+ SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
+ if (manager == null) {
+ throw new RuntimeException("Could not find SubscriptionManager.");
+ }
+ return manager;
+ }
+
+ private static ITelephony getITelephony() {
+ ITelephony binder = ITelephony.Stub.asInterface(
+ ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ if (binder == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+ return binder;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index dcd7ea7..321bfff 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -16,9 +16,9 @@
package android.telephony.ims.stub;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.content.Context;
-import android.content.Intent;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.ims.aidl.IImsConfig;
@@ -28,6 +28,8 @@
import com.android.ims.ImsConfig;
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.HashMap;
@@ -215,41 +217,6 @@
}
/**
- * Callback that the framework uses for receiving Configuration change updates.
- * {@hide}
- */
- public static class Callback extends IImsConfigCallback.Stub {
-
- @Override
- public final void onIntConfigChanged(int item, int value) throws RemoteException {
- onConfigChanged(item, value);
- }
-
- @Override
- public final void onStringConfigChanged(int item, String value) throws RemoteException {
- onConfigChanged(item, value);
- }
-
- /**
- * Called when the IMS configuration has changed.
- * @param item the IMS configuration key constant, as defined in ImsConfig.
- * @param value the new integer value of the IMS configuration constant.
- */
- public void onConfigChanged(int item, int value) {
- // Base Implementation
- }
-
- /**
- * Called when the IMS configuration has changed.
- * @param item the IMS configuration key constant, as defined in ImsConfig.
- * @param value the new String value of the IMS configuration constant.
- */
- public void onConfigChanged(int item, String value) {
- // Base Implementation
- }
- }
-
- /**
* The configuration requested resulted in an unknown result. This may happen if the
* IMS configurations are unavailable.
*/
@@ -263,6 +230,16 @@
*/
public static final int CONFIG_RESULT_FAILED = 1;
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CONFIG_RESULT_", value = {
+ CONFIG_RESULT_SUCCESS,
+ CONFIG_RESULT_FAILED
+ })
+ public @interface SetConfigResult {}
+
private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
ImsConfigStub mImsConfigStub;
@@ -279,17 +256,16 @@
}
/**
- * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration
- * changes.
+ * Adds a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks
+ * notified when a value in the configuration changes.
* @param c callback to add.
*/
private void addImsConfigCallback(IImsConfigCallback c) {
mCallbacks.register(c);
}
/**
- * Removes a {@link Callback} to the list of callbacks notified when a value in the
- * configuration changes.
- *
+ * Removes a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks
+ * notified when a value in the configuration changes.
* @param c callback to remove.
*/
private void removeImsConfigCallback(IImsConfigCallback c) {
@@ -370,10 +346,9 @@
*
* @param item an integer key.
* @param value an integer containing the configuration value.
- * @return the result of setting the configuration value, defined as either
- * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+ * @return the result of setting the configuration value.
*/
- public int setConfig(int item, int value) {
+ public @SetConfigResult int setConfig(int item, int value) {
// Base Implementation - To be overridden.
return CONFIG_RESULT_FAILED;
}
@@ -383,10 +358,9 @@
*
* @param item an integer key.
* @param value a String containing the new configuration value.
- * @return Result of setting the configuration value, defined as either
- * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+ * @return Result of setting the configuration value.
*/
- public int setConfig(int item, String value) {
+ public @SetConfigResult int setConfig(int item, String value) {
// Base Implementation - To be overridden.
return CONFIG_RESULT_FAILED;
}
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index 90e9880..71a2174 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -16,12 +16,17 @@
package com.android.ims;
-import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
import android.os.RemoteException;
import android.telephony.Rlog;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.aidl.IImsConfig;
-import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.aidl.IImsConfigCallback;
+
+import java.util.concurrent.Executor;
/**
* Provides APIs to get/set the IMS service feature/capability/parameters.
@@ -29,8 +34,10 @@
* 1) Items provisioned by the operator.
* 2) Items configured by user. Mainly service feature class.
*
+ * @deprecated Use {@link ProvisioningManager} to change these configurations in the ImsService.
* @hide
*/
+@Deprecated
public class ImsConfig {
private static final String TAG = "ImsConfig";
private boolean DBG = true;
@@ -46,7 +53,7 @@
/**
* Broadcast action: the configuration was changed
- * @deprecated Use {@link ImsConfig#addConfigCallback(ImsConfigImplBase.Callback)} instead.
+ * @deprecated Use {@link android.telephony.ims.ProvisioningManager.Callback} instead.
* @hide
*/
public static final String ACTION_IMS_CONFIG_CHANGED =
@@ -673,13 +680,25 @@
}
/**
- * Adds a {@link ImsConfigImplBase.Callback} to the ImsService to notify when a Configuration
+ * Adds a {@link ProvisioningManager.Callback} to the ImsService to notify when a Configuration
* item has changed.
*
- * Make sure to call {@link #removeConfigCallback(ImsConfigImplBase.Callback)} when finished
+ * Make sure to call {@link #removeConfigCallback(IImsConfigCallback)} when finished
* using this callback.
*/
- public void addConfigCallback(ImsConfigImplBase.Callback callback) throws ImsException {
+ public void addConfigCallback(ProvisioningManager.Callback callback) throws ImsException {
+ callback.setExecutor(getThreadExecutor());
+ addConfigCallback(callback.getBinder());
+ }
+
+ /**
+ * Adds a {@link IImsConfigCallback} to the ImsService to notify when a Configuration
+ * item has changed.
+ *
+ * Make sure to call {@link #removeConfigCallback(IImsConfigCallback)} when finished
+ * using this callback.
+ */
+ public void addConfigCallback(IImsConfigCallback callback) throws ImsException {
if (DBG) Rlog.d(TAG, "addConfigCallback: " + callback);
try {
miConfig.addImsConfigCallback(callback);
@@ -690,10 +709,9 @@
}
/**
- * Removes a {@link ImsConfigImplBase.Callback} from the ImsService that was previously added
- * by {@link #addConfigCallback(ImsConfigImplBase.Callback)}.
+ * Removes an existing {@link IImsConfigCallback} from the ImsService.
*/
- public void removeConfigCallback(ImsConfigImplBase.Callback callback) throws ImsException {
+ public void removeConfigCallback(IImsConfigCallback callback) throws ImsException {
if (DBG) Rlog.d(TAG, "removeConfigCallback: " + callback);
try {
miConfig.removeImsConfigCallback(callback);
@@ -709,4 +727,11 @@
public boolean isBinderAlive() {
return miConfig.asBinder().isBinderAlive();
}
+
+ private Executor getThreadExecutor() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ return new HandlerExecutor(new Handler(Looper.myLooper()));
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 3aaa323..85a9cf5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -40,6 +40,7 @@
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
@@ -1569,24 +1570,24 @@
/**
* Adds an IMS registration status callback for the subscription id specified.
*/
- oneway void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+ void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
String callingPackage);
/**
* Removes an existing IMS registration status callback for the subscription specified.
*/
- oneway void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+ void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
String callingPackage);
/**
* Adds an IMS MmTel capabilities callback for the subscription specified.
*/
- oneway void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+ void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
String callingPackage);
/**
* Removes an existing IMS MmTel capabilities callback for the subscription specified.
*/
- oneway void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+ void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
String callingPackage);
/**
@@ -1691,4 +1692,34 @@
* Return a list of certs in hex string from loaded carrier privileges access rules.
*/
List<String> getCertsFromCarrierPrivilegeAccessRules(int subId);
+
+ /**
+ * Register an IMS provisioning change callback with Telephony.
+ */
+ void registerImsProvisioningChangedCallback(int subId, IImsConfigCallback callback);
+
+ /**
+ * unregister an existing IMS provisioning change callback.
+ */
+ void unregisterImsProvisioningChangedCallback(int subId, IImsConfigCallback callback);
+
+ /**
+ * Return an integer containing the provisioning value for the specified provisioning key.
+ */
+ int getImsProvisioningInt(int subId, int key);
+
+ /**
+ * return a String containing the provisioning value for the provisioning key specified.
+ */
+ String getImsProvisioningString(int subId, int key);
+
+ /**
+ * Set the integer provisioning value for the provisioning key specified.
+ */
+ int setImsProvisioningInt(int subId, int key, int value);
+
+ /**
+ * Set the String provisioning value for the provisioning key specified.
+ */
+ int setImsProvisioningString(int subId, int key, String value);
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 954b51f..7919074 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -4256,27 +4256,21 @@
/**
* @return true if this device supports WPA3-Personal SAE
- * @hide
*/
- @SystemApi
public boolean isWpa3SaeSupported() {
return isFeatureSupported(WIFI_FEATURE_WPA3_SAE);
}
/**
* @return true if this device supports WPA3-Enterprise Suite-B-192
- * @hide
*/
- @SystemApi
public boolean isWpa3SuiteBSupported() {
return isFeatureSupported(WIFI_FEATURE_WPA3_SUITE_B);
}
/**
* @return true if this device supports Wi-Fi Enhanced Open (OWE)
- * @hide
*/
- @SystemApi
public boolean isOweSupported() {
return isFeatureSupported(WIFI_FEATURE_OWE);
}
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 71d4173..26bdb18 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -20,10 +20,10 @@
import android.net.wifi.hotspot2.pps.HomeSp;
import android.net.wifi.hotspot2.pps.Policy;
import android.net.wifi.hotspot2.pps.UpdateParameter;
+import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
-import android.os.Parcel;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@@ -467,24 +467,54 @@
}
/**
- * Validate the configuration data.
+ * Validate the R1 configuration data.
*
* @return true on success or false on failure
* @hide
*/
public boolean validate() {
- if (mHomeSp == null || !mHomeSp.validate()) {
- return false;
- }
- if (mCredential == null || !mCredential.validate()) {
- return false;
- }
- if (mPolicy != null && !mPolicy.validate()) {
- return false;
- }
+ // Optional: PerProviderSubscription/<X+>/SubscriptionUpdate
if (mSubscriptionUpdate != null && !mSubscriptionUpdate.validate()) {
return false;
}
+ return validateForCommonR1andR2(true);
+ }
+
+ /**
+ * Validate the R2 configuration data.
+ *
+ * @return true on success or false on failure
+ * @hide
+ */
+ public boolean validateForR2() {
+ // Required: PerProviderSubscription/UpdateIdentifier
+ if (mUpdateIdentifier == Integer.MIN_VALUE) {
+ return false;
+ }
+
+ // Required: PerProviderSubscription/<X+>/SubscriptionUpdate
+ if (mSubscriptionUpdate == null || !mSubscriptionUpdate.validate()) {
+ return false;
+ }
+ return validateForCommonR1andR2(false);
+ }
+
+ private boolean validateForCommonR1andR2(boolean isR1) {
+ // Required: PerProviderSubscription/<X+>/HomeSP
+ if (mHomeSp == null || !mHomeSp.validate()) {
+ return false;
+ }
+
+ // Required: PerProviderSubscription/<X+>/Credential
+ if (mCredential == null || !mCredential.validate(isR1)) {
+ return false;
+ }
+
+ // Optional: PerProviderSubscription/<X+>/Policy
+ if (mPolicy != null && !mPolicy.validate()) {
+ return false;
+ }
+
if (mTrustRootCertList != null) {
for (Map.Entry<String, byte[]> entry : mTrustRootCertList.entrySet()) {
String url = entry.getKey();
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index e8fcbfd..7689fc3 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -18,8 +18,8 @@
import android.net.wifi.EAPConstants;
import android.net.wifi.ParcelUtil;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
@@ -1019,10 +1019,11 @@
/**
* Validate the configuration data.
*
+ * @param isR1 {@code true} if the configuration is for R1
* @return true on success or false on failure
* @hide
*/
- public boolean validate() {
+ public boolean validate(boolean isR1) {
if (TextUtils.isEmpty(mRealm)) {
Log.d(TAG, "Missing realm");
return false;
@@ -1035,11 +1036,11 @@
// Verify the credential.
if (mUserCredential != null) {
- if (!verifyUserCredential()) {
+ if (!verifyUserCredential(isR1)) {
return false;
}
} else if (mCertCredential != null) {
- if (!verifyCertCredential()) {
+ if (!verifyCertCredential(isR1)) {
return false;
}
} else if (mSimCredential != null) {
@@ -1081,9 +1082,10 @@
/**
* Verify user credential.
*
+ * @param isR1 {@code true} if credential is for R1
* @return true if user credential is valid, false otherwise.
*/
- private boolean verifyUserCredential() {
+ private boolean verifyUserCredential(boolean isR1) {
if (mUserCredential == null) {
Log.d(TAG, "Missing user credential");
return false;
@@ -1095,7 +1097,10 @@
if (!mUserCredential.validate()) {
return false;
}
- if (mCaCertificate == null) {
+
+ // CA certificate is required for R1 Passpoint profile.
+ // For R2, it is downloaded using cert URL provided in PPS MO after validation completes.
+ if (isR1 && mCaCertificate == null) {
Log.d(TAG, "Missing CA Certificate for user credential");
return false;
}
@@ -1106,9 +1111,10 @@
* Verify certificate credential, which is used for EAP-TLS. This will verify
* that the necessary client key and certificates are provided.
*
+ * @param isR1 {@code true} if credential is for R1
* @return true if certificate credential is valid, false otherwise.
*/
- private boolean verifyCertCredential() {
+ private boolean verifyCertCredential(boolean isR1) {
if (mCertCredential == null) {
Log.d(TAG, "Missing certificate credential");
return false;
@@ -1123,7 +1129,9 @@
}
// Verify required key and certificates for certificate credential.
- if (mCaCertificate == null) {
+ // CA certificate is required for R1 Passpoint profile.
+ // For R2, it is downloaded using cert URL provided in PPS MO after validation completes.
+ if (isR1 && mCaCertificate == null) {
Log.d(TAG, "Missing CA Certificate for certificate credential");
return false;
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index 940adc8..775ce21 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -146,6 +146,7 @@
*/
private static PasspointConfiguration createConfig() {
PasspointConfiguration config = new PasspointConfiguration();
+ config.setUpdateIdentifier(1234);
config.setHomeSp(createHomeSp());
config.setCredential(createCredential());
config.setPolicy(createPolicy());
@@ -273,18 +274,37 @@
@Test
public void validateDefaultConfig() throws Exception {
PasspointConfiguration config = new PasspointConfiguration();
+
assertFalse(config.validate());
+ assertFalse(config.validateForR2());
}
/**
- * Verify that a configuration contained all fields is valid.
+ * Verify that a configuration containing all fields is valid for R1/R2.
*
* @throws Exception
*/
@Test
public void validateFullConfig() throws Exception {
PasspointConfiguration config = createConfig();
+
assertTrue(config.validate());
+ assertTrue(config.validateForR2());
+ }
+
+ /**
+ * Verify that a configuration containing all fields except for UpdateIdentifier is valid for
+ * R1, but invalid for R2.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateFullConfigWithoutUpdateIdentifier() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.setUpdateIdentifier(Integer.MIN_VALUE);
+
+ assertTrue(config.validate());
+ assertFalse(config.validateForR2());
}
/**
@@ -296,7 +316,9 @@
public void validateConfigWithoutCredential() throws Exception {
PasspointConfiguration config = createConfig();
config.setCredential(null);
+
assertFalse(config.validate());
+ assertFalse(config.validateForR2());
}
/**
@@ -308,12 +330,14 @@
public void validateConfigWithoutHomeSp() throws Exception {
PasspointConfiguration config = createConfig();
config.setHomeSp(null);
+
assertFalse(config.validate());
+ assertFalse(config.validateForR2());
}
/**
* Verify that a configuration without Policy is valid, since Policy configurations
- * are optional (applied for Hotspot 2.0 Release only).
+ * are optional for R1 and R2.
*
* @throws Exception
*/
@@ -321,12 +345,14 @@
public void validateConfigWithoutPolicy() throws Exception {
PasspointConfiguration config = createConfig();
config.setPolicy(null);
+
assertTrue(config.validate());
+ assertTrue(config.validateForR2());
}
/**
- * Verify that a configuration without subscription update is valid, since subscription
- * update configurations are optional (applied for Hotspot 2.0 Release only).
+ * Verify that a configuration without subscription update is valid for R1 and invalid for R2,
+ * since subscription update configuration is only applicable for R2.
*
* @throws Exception
*/
@@ -334,7 +360,9 @@
public void validateConfigWithoutSubscriptionUpdate() throws Exception {
PasspointConfiguration config = createConfig();
config.setSubscriptionUpdate(null);
+
assertTrue(config.validate());
+ assertFalse(config.validateForR2());
}
/**
@@ -352,12 +380,15 @@
trustRootCertList.put(new String(rawUrlBytes, StandardCharsets.UTF_8),
new byte[CERTIFICATE_FINGERPRINT_BYTES]);
config.setTrustRootCertList(trustRootCertList);
+
assertFalse(config.validate());
trustRootCertList = new HashMap<>();
trustRootCertList.put(null, new byte[CERTIFICATE_FINGERPRINT_BYTES]);
config.setTrustRootCertList(trustRootCertList);
+
assertFalse(config.validate());
+ assertFalse(config.validateForR2());
}
/**
@@ -382,6 +413,7 @@
trustRootCertList.put("test.cert.com", null);
config.setTrustRootCertList(trustRootCertList);
assertFalse(config.validate());
+ assertFalse(config.validateForR2());
}
/**
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index c5ad7de..c07db6c 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -24,14 +24,13 @@
import android.os.Parcel;
import android.support.test.filters.SmallTest;
+import org.junit.Test;
+
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
-import java.util.Arrays;
-
-import org.junit.Test;
/**
* Unit tests for {@link android.net.wifi.hotspot2.pps.CredentialTest}.
@@ -169,7 +168,12 @@
@Test
public void validateUserCredential() throws Exception {
Credential cred = createCredentialWithUserCredential();
- assertTrue(cred.validate());
+
+ // For R1 validation
+ assertTrue(cred.validate(true));
+
+ // For R2 validation
+ assertTrue(cred.validate(false));
}
/**
@@ -181,7 +185,12 @@
public void validateUserCredentialWithoutCaCert() throws Exception {
Credential cred = createCredentialWithUserCredential();
cred.setCaCertificate(null);
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertTrue(cred.validate(false));
}
/**
@@ -193,7 +202,12 @@
public void validateUserCredentialWithEapTls() throws Exception {
Credential cred = createCredentialWithUserCredential();
cred.getUserCredential().setEapType(EAPConstants.EAP_TLS);
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertFalse(cred.validate(false));
}
@@ -206,7 +220,12 @@
public void validateUserCredentialWithoutRealm() throws Exception {
Credential cred = createCredentialWithUserCredential();
cred.setRealm(null);
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertFalse(cred.validate(false));
}
/**
@@ -218,7 +237,12 @@
public void validateUserCredentialWithoutUsername() throws Exception {
Credential cred = createCredentialWithUserCredential();
cred.getUserCredential().setUsername(null);
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertFalse(cred.validate(false));
}
/**
@@ -230,7 +254,12 @@
public void validateUserCredentialWithoutPassword() throws Exception {
Credential cred = createCredentialWithUserCredential();
cred.getUserCredential().setPassword(null);
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertFalse(cred.validate(false));
}
/**
@@ -242,7 +271,12 @@
public void validateUserCredentialWithoutAuthMethod() throws Exception {
Credential cred = createCredentialWithUserCredential();
cred.getUserCredential().setNonEapInnerMethod(null);
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertFalse(cred.validate(false));
}
/**
@@ -255,7 +289,12 @@
@Test
public void validateCertCredential() throws Exception {
Credential cred = createCredentialWithCertificateCredential();
- assertTrue(cred.validate());
+
+ // For R1 validation
+ assertTrue(cred.validate(true));
+
+ // For R2 validation
+ assertTrue(cred.validate(true));
}
/**
@@ -267,7 +306,12 @@
public void validateCertCredentialWithoutCaCert() throws Exception {
Credential cred = createCredentialWithCertificateCredential();
cred.setCaCertificate(null);
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertTrue(cred.validate(false));
}
/**
@@ -279,7 +323,12 @@
public void validateCertCredentialWithoutClientCertChain() throws Exception {
Credential cred = createCredentialWithCertificateCredential();
cred.setClientCertificateChain(null);
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertFalse(cred.validate(false));
}
/**
@@ -291,7 +340,12 @@
public void validateCertCredentialWithoutClientPrivateKey() throws Exception {
Credential cred = createCredentialWithCertificateCredential();
cred.setClientPrivateKey(null);
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertFalse(cred.validate(false));
}
/**
@@ -304,7 +358,12 @@
public void validateCertCredentialWithMismatchFingerprint() throws Exception {
Credential cred = createCredentialWithCertificateCredential();
cred.getCertCredential().setCertSha256Fingerprint(new byte[32]);
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertFalse(cred.validate(false));
}
/**
@@ -315,7 +374,12 @@
@Test
public void validateSimCredentialWithEapSim() throws Exception {
Credential cred = createCredentialWithSimCredential();
- assertTrue(cred.validate());
+
+ // For R1 validation
+ assertTrue(cred.validate(true));
+
+ // For R2 validation
+ assertTrue(cred.validate(false));
}
/**
@@ -327,7 +391,12 @@
public void validateSimCredentialWithEapAka() throws Exception {
Credential cred = createCredentialWithSimCredential();
cred.getSimCredential().setEapType(EAPConstants.EAP_AKA);
- assertTrue(cred.validate());
+
+ // For R1 validation
+ assertTrue(cred.validate(true));
+
+ // For R2 validation
+ assertTrue(cred.validate(false));
}
/**
@@ -339,7 +408,12 @@
public void validateSimCredentialWithEapAkaPrime() throws Exception {
Credential cred = createCredentialWithSimCredential();
cred.getSimCredential().setEapType(EAPConstants.EAP_AKA_PRIME);
- assertTrue(cred.validate());
+
+ // For R1 validation
+ assertTrue(cred.validate(true));
+
+ // For R2 validation
+ assertTrue(cred.validate(false));
}
/**
@@ -351,7 +425,12 @@
public void validateSimCredentialWithoutIMSI() throws Exception {
Credential cred = createCredentialWithSimCredential();
cred.getSimCredential().setImsi(null);
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertFalse(cred.validate(false));
}
/**
@@ -363,7 +442,12 @@
public void validateSimCredentialWithInvalidIMSI() throws Exception {
Credential cred = createCredentialWithSimCredential();
cred.getSimCredential().setImsi("dummy");
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertFalse(cred.validate(false));
}
/**
@@ -375,7 +459,12 @@
public void validateSimCredentialWithEapTls() throws Exception {
Credential cred = createCredentialWithSimCredential();
cred.getSimCredential().setEapType(EAPConstants.EAP_TLS);
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertFalse(cred.validate(false));
}
/**
@@ -391,7 +480,12 @@
simCredential.setImsi("1234*");
simCredential.setEapType(EAPConstants.EAP_SIM);
cred.setSimCredential(simCredential);
- assertFalse(cred.validate());
+
+ // For R1 validation
+ assertFalse(cred.validate(true));
+
+ // For R2 validation
+ assertFalse(cred.validate(false));
}
/**