Merge "Add time until Night Light is on/off as secondary label"
diff --git a/Android.bp b/Android.bp
index ae4f974..6517a0c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -117,7 +117,7 @@
"core/java/android/content/ISyncServiceAdapter.aidl",
"core/java/android/content/ISyncStatusObserver.aidl",
"core/java/android/content/om/IOverlayManager.aidl",
- "core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl",
+ "core/java/android/content/pm/ICrossProfileApps.aidl",
"core/java/android/content/pm/IDexModuleRegisterCallback.aidl",
"core/java/android/content/pm/ILauncherApps.aidl",
"core/java/android/content/pm/IOnAppsChangedListener.aidl",
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 93a0fc3..0ac167f 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -190,8 +190,11 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = MeasuredText.build(
- generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
+ final MeasuredText text = new MeasuredText.Builder(
+ generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build();
state.resumeTiming();
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -206,8 +209,11 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = MeasuredText.build(
- generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
+ final MeasuredText text = new MeasuredText.Builder(
+ generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .build();
state.resumeTiming();
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -222,8 +228,11 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = MeasuredText.build(
- generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
+ final MeasuredText text = new MeasuredText.Builder(
+ generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build();
state.resumeTiming();
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -238,8 +247,11 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = MeasuredText.build(
- generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
+ final MeasuredText text = new MeasuredText.Builder(
+ generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .build();
state.resumeTiming();
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -254,8 +266,11 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = MeasuredText.build(
- generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT, LTR);
+ final MeasuredText text = new MeasuredText.Builder(
+ generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build();
state.resumeTiming();
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
diff --git a/api/current.txt b/api/current.txt
index 1fb64ed..06f3b2a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5859,11 +5859,15 @@
method public java.lang.CharSequence getLabel();
method public java.lang.String getResultKey();
method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
+ method public static int getResultsSource(android.content.Intent);
method public boolean isDataOnly();
+ method public static void setResultsSource(android.content.Intent, int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR;
field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+ field public static final int SOURCE_CHOICE = 1; // 0x1
+ field public static final int SOURCE_FREE_FORM_INPUT = 0; // 0x0
}
public static final class RemoteInput.Builder {
@@ -10683,6 +10687,13 @@
field public int reqTouchScreen;
}
+ public class CrossProfileApps {
+ method public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(android.os.UserHandle);
+ method public java.lang.CharSequence getProfileSwitchingLabel(android.os.UserHandle);
+ method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
+ method public void startMainActivity(android.content.ComponentName, android.os.UserHandle);
+ }
+
public final class FeatureGroupInfo implements android.os.Parcelable {
ctor public FeatureGroupInfo();
ctor public FeatureGroupInfo(android.content.pm.FeatureGroupInfo);
@@ -11477,17 +11488,6 @@
}
-package android.content.pm.crossprofile {
-
- public class CrossProfileApps {
- method public android.graphics.drawable.Drawable getProfileSwitchingIcon(android.os.UserHandle);
- method public java.lang.CharSequence getProfileSwitchingLabel(android.os.UserHandle);
- method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
- method public void startMainActivity(android.content.ComponentName, android.os.UserHandle);
- }
-
-}
-
package android.content.res {
public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -42316,10 +42316,10 @@
}
public class MeasuredText implements android.text.Spanned {
- method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic);
- method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic, int, int);
method public char charAt(int);
+ method public int getBreakStrategy();
method public int getEnd();
+ method public int getHyphenationFrequency();
method public android.text.TextPaint getPaint();
method public int getParagraphCount();
method public int getParagraphEnd(int);
@@ -42336,6 +42336,15 @@
method public java.lang.CharSequence subSequence(int, int);
}
+ public static final class MeasuredText.Builder {
+ ctor public MeasuredText.Builder(java.lang.CharSequence, android.text.TextPaint);
+ method public android.text.MeasuredText build();
+ method public android.text.MeasuredText.Builder setBreakStrategy(int);
+ method public android.text.MeasuredText.Builder setHyphenationFrequency(int);
+ method public android.text.MeasuredText.Builder setRange(int, int);
+ method public android.text.MeasuredText.Builder setTextDirection(android.text.TextDirectionHeuristic);
+ }
+
public abstract interface NoCopySpan {
}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 2c35d41..9bddbcb 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -36,7 +36,7 @@
src/config/ConfigManager.cpp \
src/external/StatsPuller.cpp \
src/external/StatsCompanionServicePuller.cpp \
- src/external/ResourcePowerManagerPuller.cpp \
+ src/external/SubsystemSleepStatePuller.cpp \
src/external/CpuTimePerUidPuller.cpp \
src/external/CpuTimePerUidFreqPuller.cpp \
src/external/StatsPullerManagerImpl.cpp \
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 0ed1c1f..ca097d0 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -185,6 +185,7 @@
*/
void StatsService::dump_impl(FILE* out) {
mConfigManager->Dump(out);
+ StatsdStats::getInstance().dumpStats(out);
}
/**
@@ -296,9 +297,8 @@
fprintf(out, " NAME The name of the configuration\n");
fprintf(out, "\n");
fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats print-stats [reset]\n");
+ fprintf(out, "usage: adb shell cmd stats print-stats\n");
fprintf(out, " Prints some basic stats.\n");
- fprintf(out, " reset: 1 to reset the statsd stats. default=0.\n");
}
status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) {
@@ -487,14 +487,8 @@
fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
mProcessor->GetMetricsSize(key));
}
- fprintf(out, "Detailed statsd stats in logcat...\n");
StatsdStats& statsdStats = StatsdStats::getInstance();
- bool reset = false;
- if (args.size() > 1) {
- reset = strtol(args[1].string(), NULL, 10);
- }
- vector<uint8_t> output;
- statsdStats.dumpStats(&output, reset);
+ statsdStats.dumpStats(out);
return NO_ERROR;
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 4a7f0c4..7d513cd 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -94,9 +94,8 @@
MobileBytesTransfer mobile_bytes_transfer = 10002;
MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg = 10003;
KernelWakelock kernel_wakelock = 10004;
- PlatformSleepState platform_sleep_state = 10005;
- SleepStateVoter sleep_state_voter = 10006;
- SubsystemSleepState subsystem_sleep_state = 10007;
+ SubsystemSleepState subsystem_sleep_state = 10005;
+ // 10006 and 10007 are free to use.
CpuTimePerFreq cpu_time_per_freq = 10008;
CpuTimePerUid cpu_time_per_uid = 10009;
CpuTimePerUidFreq cpu_time_per_uid_freq = 10010;
@@ -1027,44 +1026,21 @@
}
/**
- * Pulls PowerStatePlatformSleepState.
- *
- * Definition here:
+ * Pulls low power state information. This includes platform and subsystem sleep state information,
+ * PowerStatePlatformSleepState, PowerStateVoter or PowerStateSubsystemSleepState as defined in
* hardware/interfaces/power/1.0/types.hal
- */
-message PlatformSleepState {
- optional string name = 1;
- optional uint64 residency_in_msec_since_boot = 2;
- optional uint64 total_transitions = 3;
- optional bool supported_only_in_suspend = 4;
-}
-
-/**
- * Pulls PowerStateVoter.
- *
- * Definition here:
- * hardware/interfaces/power/1.0/types.hal
- */
-message SleepStateVoter {
- optional string platform_sleep_state_name = 1;
- optional string voter_name = 2;
- optional uint64 total_time_in_msec_voted_for_since_boot = 3;
- optional uint64 total_number_of_times_voted_since_boot = 4;
-}
-
-/**
- * Pulls PowerStateSubsystemSleepState.
- *
- * Definition here:
* hardware/interfaces/power/1.1/types.hal
*/
message SubsystemSleepState {
- optional string subsystem_name = 1;
- optional string subsystem_sleep_state_name = 2;
- optional uint64 residency_in_msec_since_boot = 3;
- optional uint64 total_transitions = 4;
- optional uint64 last_entry_timestamp_ms = 5;
- optional bool supported_only_in_suspend = 6;
+ // Name should be in the format of XXX.YYY where XXX is subsystem name,
+ // YYY is corresponding voter name.
+ // If there are no voters, the format should just be XXX (with no dot).
+ // XXX and YYY should not contain a "." in it.
+ optional string name = 1;
+ // The number of times it entered, or voted for entering the sleep state
+ optional uint64 count = 2;
+ // The length of time spent in, or spent voting for, the sleep state
+ optional uint64 timeMs = 3;
}
/**
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 58c7b12..bb2e8c0 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -23,7 +23,7 @@
#include <climits>
#include "CpuTimePerUidFreqPuller.h"
#include "CpuTimePerUidPuller.h"
-#include "ResourcePowerManagerPuller.h"
+#include "SubsystemSleepStatePuller.h"
#include "StatsCompanionServicePuller.h"
#include "StatsPullerManagerImpl.h"
#include "StatsService.h"
@@ -58,13 +58,9 @@
mPullers.insert({android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
make_shared<StatsCompanionServicePuller>(
android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)});
- mPullers.insert({android::util::PLATFORM_SLEEP_STATE,
- make_shared<ResourcePowerManagerPuller>(android::util::PLATFORM_SLEEP_STATE)});
- mPullers.insert({android::util::SLEEP_STATE_VOTER,
- make_shared<ResourcePowerManagerPuller>(android::util::SLEEP_STATE_VOTER)});
mPullers.insert(
{android::util::SUBSYSTEM_SLEEP_STATE,
- make_shared<ResourcePowerManagerPuller>(android::util::SUBSYSTEM_SLEEP_STATE)});
+ make_shared<SubsystemSleepStatePuller>()});
mPullers.insert({android::util::CPU_TIME_PER_FREQ, make_shared<StatsCompanionServicePuller>(android::util::CPU_TIME_PER_FREQ)});
mPullers.insert({android::util::CPU_TIME_PER_UID, make_shared<CpuTimePerUidPuller>()});
mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, make_shared<CpuTimePerUidFreqPuller>()});
diff --git a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
similarity index 85%
rename from cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
rename to cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
index 2e29fb0..6d12e25 100644
--- a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
+++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
@@ -30,10 +30,10 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
-#include "external/ResourcePowerManagerPuller.h"
+#include "external/SubsystemSleepStatePuller.h"
#include "external/StatsPuller.h"
-#include "ResourcePowerManagerPuller.h"
+#include "SubsystemSleepStatePuller.h"
#include "logd/LogEvent.h"
#include "statslog.h"
@@ -73,10 +73,10 @@
return gPowerHalV1_0 != nullptr;
}
-ResourcePowerManagerPuller::ResourcePowerManagerPuller(int tagId) : StatsPuller(tagId) {
+SubsystemSleepStatePuller::SubsystemSleepStatePuller() : StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) {
}
-bool ResourcePowerManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
std::lock_guard<std::mutex> lock(gPowerHalMutex);
if (!getPowerHal()) {
@@ -89,8 +89,6 @@
data->clear();
Return<void> ret;
- if (mTagId == android::util::PLATFORM_SLEEP_STATE ||
- mTagId == android::util::SLEEP_STATE_VOTER) {
ret = gPowerHalV1_0->getPlatformLowPowerStats(
[&data, timestamp](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
if (status != Status::SUCCESS) return;
@@ -98,12 +96,11 @@
for (size_t i = 0; i < states.size(); i++) {
const PowerStatePlatformSleepState& state = states[i];
- auto statePtr = make_shared<LogEvent>(android::util::PLATFORM_SLEEP_STATE,
+ auto statePtr = make_shared<LogEvent>(android::util::SUBSYSTEM_SLEEP_STATE,
timestamp);
statePtr->write(state.name);
- statePtr->write(state.residencyInMsecSinceBoot);
statePtr->write(state.totalTransitions);
- statePtr->write(state.supportedOnlyInSuspend);
+ statePtr->write(state.residencyInMsecSinceBoot);
statePtr->init();
data->push_back(statePtr);
VLOG("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
@@ -111,12 +108,11 @@
(long long)state.totalTransitions,
state.supportedOnlyInSuspend ? 1 : 0);
for (auto voter : state.voters) {
- auto voterPtr = make_shared<LogEvent>(android::util::SLEEP_STATE_VOTER,
+ auto voterPtr = make_shared<LogEvent>(android::util::SUBSYSTEM_SLEEP_STATE,
timestamp);
- voterPtr->write(state.name);
- voterPtr->write(voter.name);
- voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot);
+ voterPtr->write((std::string)state.name + "." + (std::string)voter.name);
voterPtr->write(voter.totalNumberOfTimesVotedSinceBoot);
+ voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot);
voterPtr->init();
data->push_back(voterPtr);
VLOG("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
@@ -131,9 +127,7 @@
gPowerHalV1_0 = nullptr;
return false;
}
- }
- if (mTagId == android::util::SUBSYSTEM_SLEEP_STATE) {
// Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1
sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1 =
android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
@@ -149,13 +143,10 @@
const PowerStateSubsystemSleepState& state =
subsystem.states[j];
auto subsystemStatePtr = make_shared<LogEvent>(
- android::util::SUBSYSTEM_SLEEP_STATE, timestamp);
- subsystemStatePtr->write(subsystem.name);
- subsystemStatePtr->write(state.name);
- subsystemStatePtr->write(state.residencyInMsecSinceBoot);
+ android::util::SUBSYSTEM_SLEEP_STATE, timestamp);
+ subsystemStatePtr->write((std::string)subsystem.name + "." + (std::string)state.name);
subsystemStatePtr->write(state.totalTransitions);
- subsystemStatePtr->write(state.lastEntryTimestampMs);
- subsystemStatePtr->write(state.supportedOnlyInSuspend);
+ subsystemStatePtr->write(state.residencyInMsecSinceBoot);
subsystemStatePtr->init();
data->push_back(subsystemStatePtr);
VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
@@ -168,7 +159,6 @@
}
});
}
- }
return true;
}
diff --git a/cmds/statsd/src/external/ResourcePowerManagerPuller.h b/cmds/statsd/src/external/SubsystemSleepStatePuller.h
similarity index 90%
rename from cmds/statsd/src/external/ResourcePowerManagerPuller.h
rename to cmds/statsd/src/external/SubsystemSleepStatePuller.h
index 3399408..17ce5b4 100644
--- a/cmds/statsd/src/external/ResourcePowerManagerPuller.h
+++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.h
@@ -26,9 +26,9 @@
/**
* Reads hal for sleep states
*/
-class ResourcePowerManagerPuller : public StatsPuller {
+class SubsystemSleepStatePuller : public StatsPuller {
public:
- ResourcePowerManagerPuller(int tagId);
+ SubsystemSleepStatePuller();
bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
};
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 36dd616..63bde7d 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -66,8 +66,6 @@
{android::util::MOBILE_BYTES_TRANSFER, 1},
{android::util::WIFI_BYTES_TRANSFER_BY_FG_BG, 1},
{android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG, 1},
- {android::util::PLATFORM_SLEEP_STATE, 1},
- {android::util::SLEEP_STATE_VOTER, 1},
{android::util::SUBSYSTEM_SLEEP_STATE, 1},
{android::util::CPU_TIME_PER_FREQ, 1},
{android::util::CPU_TIME_PER_UID, 1},
@@ -357,73 +355,135 @@
}
}
+void StatsdStats::dumpStats(FILE* out) const {
+ lock_guard<std::mutex> lock(mLock);
+ time_t t = mStartTimeSec;
+ struct tm* tm = localtime(&t);
+ char timeBuffer[80];
+ strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p\n", tm);
+ fprintf(out, "Stats collection start second: %s\n", timeBuffer);
+ fprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size());
+ for (const auto& configStats : mIceBox) {
+ fprintf(out,
+ "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
+ "#matcher=%d, #alert=%d, valid=%d\n",
+ configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(),
+ configStats.deletion_time_sec(), configStats.metric_count(),
+ configStats.condition_count(), configStats.matcher_count(),
+ configStats.alert_count(), configStats.is_valid());
+
+ for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
+ fprintf(out, "\tbroadcast time: %d\n", broadcastTime);
+ }
+
+ for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
+ fprintf(out, "\tdata drop time: %d\n", dataDropTime);
+ }
+ }
+ fprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size());
+ for (auto& pair : mConfigStats) {
+ auto& key = pair.first;
+ auto& configStats = pair.second;
+
+ fprintf(out,
+ "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
+ "#matcher=%d, #alert=%d, valid=%d\n",
+ configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(),
+ configStats.deletion_time_sec(), configStats.metric_count(),
+ configStats.condition_count(), configStats.matcher_count(),
+ configStats.alert_count(), configStats.is_valid());
+ for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
+ fprintf(out, "\tbroadcast time: %d\n", broadcastTime);
+ }
+
+ for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
+ fprintf(out, "\tdata drop time: %d\n", dataDropTime);
+ }
+
+ for (const auto& dumpTime : configStats.dump_report_time_sec()) {
+ fprintf(out, "\tdump report time: %d\n", dumpTime);
+ }
+
+ // Add matcher stats
+ auto matcherIt = mMatcherStats.find(key);
+ if (matcherIt != mMatcherStats.end()) {
+ const auto& matcherStats = matcherIt->second;
+ for (const auto& stats : matcherStats) {
+ fprintf(out, "matcher %lld matched %d times\n", (long long)stats.first,
+ stats.second);
+ }
+ }
+ // Add condition stats
+ auto conditionIt = mConditionStats.find(key);
+ if (conditionIt != mConditionStats.end()) {
+ const auto& conditionStats = conditionIt->second;
+ for (const auto& stats : conditionStats) {
+ fprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first,
+ stats.second);
+ }
+ }
+ // Add metrics stats
+ auto metricIt = mMetricsStats.find(key);
+ if (metricIt != mMetricsStats.end()) {
+ const auto& conditionStats = metricIt->second;
+ for (const auto& stats : conditionStats) {
+ fprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first,
+ stats.second);
+ }
+ }
+ // Add anomaly detection alert stats
+ auto alertIt = mAlertStats.find(key);
+ if (alertIt != mAlertStats.end()) {
+ const auto& alertStats = alertIt->second;
+ for (const auto& stats : alertStats) {
+ fprintf(out, "alert %lld declared %d times\n", (long long)stats.first,
+ stats.second);
+ }
+ }
+ }
+ fprintf(out, "********Pushed Atom stats***********\n");
+ const size_t atomCounts = mPushedAtomStats.size();
+ for (size_t i = 2; i < atomCounts; i++) {
+ if (mPushedAtomStats[i] > 0) {
+ fprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
+ }
+ }
+
+ fprintf(out, "********Pulled Atom stats***********\n");
+ for (const auto& pair : mPulledAtomStats) {
+ fprintf(out, "Atom %d->%ld, %ld, %ld\n", (int)pair.first, (long)pair.second.totalPull,
+ (long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec);
+ }
+
+ if (mAnomalyAlarmRegisteredStats > 0) {
+ fprintf(out, "********AnomalyAlarmStats stats***********\n");
+ fprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats);
+ }
+
+ fprintf(out,
+ "UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes "
+ "lost=%d\n",
+ mUidMapStats.bytes_used(), mUidMapStats.snapshots(), mUidMapStats.changes(),
+ mUidMapStats.dropped_snapshots(), mUidMapStats.dropped_changes());
+}
+
void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
lock_guard<std::mutex> lock(mLock);
- if (DEBUG) {
- time_t t = mStartTimeSec;
- struct tm* tm = localtime(&t);
- char timeBuffer[80];
- strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p", tm);
- VLOG("=================StatsdStats dump begins====================");
- VLOG("Stats collection start second: %s", timeBuffer);
- }
ProtoOutputStream proto;
proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTimeSec);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)time(nullptr));
- VLOG("%lu Config in icebox: ", (unsigned long)mIceBox.size());
for (const auto& configStats : mIceBox) {
const int numBytes = configStats.ByteSize();
vector<char> buffer(numBytes);
configStats.SerializeToArray(&buffer[0], numBytes);
proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0],
buffer.size());
-
- // surround the whole block with DEBUG, so that compiler can strip out the code
- // in production.
- if (DEBUG) {
- VLOG("*****ICEBOX*****");
- VLOG("Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
- "#matcher=%d, #alert=%d, #valid=%d",
- configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(),
- configStats.deletion_time_sec(), configStats.metric_count(),
- configStats.condition_count(), configStats.matcher_count(),
- configStats.alert_count(), configStats.is_valid());
-
- for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
- VLOG("\tbroadcast time: %d", broadcastTime);
- }
-
- for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
- VLOG("\tdata drop time: %d", dataDropTime);
- }
- }
}
for (auto& pair : mConfigStats) {
auto& configStats = pair.second;
- if (DEBUG) {
- VLOG("********Active Configs***********");
- VLOG("Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
- "#matcher=%d, #alert=%d, #valid=%d",
- configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(),
- configStats.deletion_time_sec(), configStats.metric_count(),
- configStats.condition_count(), configStats.matcher_count(),
- configStats.alert_count(), configStats.is_valid());
- for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
- VLOG("\tbroadcast time: %d", broadcastTime);
- }
-
- for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
- VLOG("\tdata drop time: %d", dataDropTime);
- }
-
- for (const auto& dumpTime : configStats.dump_report_time_sec()) {
- VLOG("\tdump report time: %d", dumpTime);
- }
- }
-
addSubStatsToConfigLocked(pair.first, configStats);
const int numBytes = configStats.ByteSize();
@@ -439,7 +499,6 @@
configStats.clear_alert_stats();
}
- VLOG("********Pushed Atom stats***********");
const size_t atomCounts = mPushedAtomStats.size();
for (size_t i = 2; i < atomCounts; i++) {
if (mPushedAtomStats[i] > 0) {
@@ -448,34 +507,24 @@
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]);
proto.end(token);
-
- VLOG("Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
}
}
- VLOG("********Pulled Atom stats***********");
for (const auto& pair : mPulledAtomStats) {
android::os::statsd::writePullerStatsToStream(pair, &proto);
- VLOG("Atom %d->%ld, %ld, %ld\n", (int) pair.first, (long) pair.second.totalPull, (long) pair.second.totalPullFromCache, (long) pair.second.minPullIntervalSec);
}
if (mAnomalyAlarmRegisteredStats > 0) {
- VLOG("********AnomalyAlarmStats stats***********");
long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ANOMALY_ALARM_STATS);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ANOMALY_ALARMS_REGISTERED,
mAnomalyAlarmRegisteredStats);
proto.end(token);
- VLOG("Anomaly alarm registrations: %d", mAnomalyAlarmRegisteredStats);
}
const int numBytes = mUidMapStats.ByteSize();
vector<char> buffer(numBytes);
mUidMapStats.SerializeToArray(&buffer[0], numBytes);
proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS, &buffer[0], buffer.size());
- VLOG("UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes "
- "lost=%d",
- mUidMapStats.bytes_used(), mUidMapStats.snapshots(), mUidMapStats.changes(),
- mUidMapStats.dropped_snapshots(), mUidMapStats.dropped_changes());
output->clear();
size_t bufferSize = proto.size();
@@ -495,7 +544,6 @@
}
VLOG("reset=%d, returned proto size %lu", reset, (unsigned long)bufferSize);
- VLOG("=================StatsdStats dump ends====================");
}
} // namespace statsd
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 52ab253..9178daa4 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -189,6 +189,11 @@
*/
void dumpStats(std::vector<uint8_t>* buffer, bool reset);
+ /**
+ * Output statsd stats in human readable format to [out] file.
+ */
+ void dumpStats(FILE* out) const;
+
typedef struct {
long totalPull;
long totalPullFromCache;
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index c542db2..1d75e20 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -111,20 +111,21 @@
int index = 0;
int uid = 0;
- string configName;
+ int64_t configID = 0;
char* substr = strtok(name, "-");
// Timestamp lives at index 2 but we skip parsing it as it's not needed.
while (substr != nullptr && index < 2) {
- if (index) {
+ if (index == 0) {
uid = atoi(substr);
- } else {
- configName = substr;
+ } else if (index == 1) {
+ configID = StrToInt64(substr);
}
index++;
+ substr = strtok(nullptr, "-");
}
if (index < 2) continue;
- sendBroadcast(ConfigKey(uid, StrToInt64(configName)));
+ sendBroadcast(ConfigKey(uid, configID));
}
}
@@ -143,19 +144,23 @@
int index = 0;
int uid = 0;
- string configName;
+ int64_t configID = 0;
+ int64_t timestamp = 0;
char* substr = strtok(name, "-");
- // Timestamp lives at index 2 but we skip parsing it as it's not needed.
- while (substr != nullptr && index < 2) {
- if (index) {
+ while (substr != nullptr && index < 3) {
+ if (index == 0) {
uid = atoi(substr);
- } else {
- configName = substr;
+ } else if (index == 1) {
+ configID = StrToInt64(substr);
+ } else if (index == 2) {
+ timestamp = atoi(substr);
}
index++;
+ substr = strtok(nullptr, "-");
}
- if (index < 2) continue;
- string file_name = StringPrintf("%s/%s", path, name);
+ if (index < 3) continue;
+ string file_name = StringPrintf("%s/%d-%lld-%lld", STATS_SERVICE_DIR, uid,
+ (long long)configID, (long long)timestamp);
int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
if (fd != -1) {
string content;
@@ -186,29 +191,32 @@
int index = 0;
int uid = 0;
- string configName;
+ int64_t configID = 0;
+ int64_t timestamp = 0;
char* substr = strtok(name, "-");
- // Timestamp lives at index 2 but we skip parsing it as it's not needed.
- while (substr != nullptr && index < 2) {
- if (index) {
+ while (substr != nullptr && index < 3) {
+ if (index == 0) {
uid = atoi(substr);
- } else {
- configName = substr;
+ } else if (index == 1) {
+ configID = StrToInt64(substr);
+ } else if (index == 2) {
+ timestamp = atoi(substr);
}
index++;
+ substr = strtok(nullptr, "-");
}
- if (index < 2) continue;
+ if (index < 3) continue;
- string file_name = StringPrintf("%s/%s", STATS_SERVICE_DIR, name);
- VLOG("full file %s", file_name.c_str());
+ string file_name = StringPrintf("%s/%d-%lld-%lld", STATS_SERVICE_DIR, uid,
+ (long long)configID, (long long)timestamp);
int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
if (fd != -1) {
string content;
if (android::base::ReadFdToString(fd, &content)) {
StatsdConfig config;
if (config.ParseFromString(content)) {
- configsMap[ConfigKey(uid, StrToInt64(configName))] = config;
- VLOG("map key uid=%d|name=%s", uid, name);
+ configsMap[ConfigKey(uid, configID)] = config;
+ VLOG("map key uid=%d|configID=%lld", uid, (long long)configID);
}
}
close(fd);
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index cc02f34..62bdba4 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -91,7 +91,7 @@
{
InSequence s;
- manager->Startup();
+ manager->StartupForTest();
// Add another one
EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, StringToId("zzz")),
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index db12c37..ccb54f9 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -334,4 +334,9 @@
* Sets the user switcher message for switching to {@link android.os.UserHandle#SYSTEM}.
*/
public abstract void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage);
+
+ /**
+ * Returns maximum number of users that can run simultaneously.
+ */
+ public abstract int getMaxRunningUsers();
}
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
index 02a0124..b7100e6 100644
--- a/core/java/android/app/RemoteInput.java
+++ b/core/java/android/app/RemoteInput.java
@@ -24,6 +24,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
+
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -73,6 +74,15 @@
private static final String EXTRA_DATA_TYPE_RESULTS_DATA =
"android.remoteinput.dataTypeResultsData";
+ /** Extra added to a clip data intent object identifying the source of the results. */
+ private static final String EXTRA_RESULTS_SOURCE = "android.remoteinput.resultsSource";
+
+ /** The user manually entered the data. */
+ public static final int SOURCE_FREE_FORM_INPUT = 0;
+
+ /** The user selected one of the choices from {@link #getChoices}. */
+ public static final int SOURCE_CHOICE = 1;
+
// Flags bitwise-ored to mFlags
private static final int FLAG_ALLOW_FREE_FORM_INPUT = 0x1;
@@ -416,6 +426,48 @@
intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent));
}
+ /**
+ * Set the source of the RemoteInput results. This method should only be called by remote
+ * input collection services (e.g.
+ * {@link android.service.notification.NotificationListenerService})
+ * when sending results to a pending intent.
+ *
+ * @see #SOURCE_FREE_FORM_INPUT
+ * @see #SOURCE_CHOICE
+ *
+ * @param intent The intent to add remote input source to. The {@link ClipData}
+ * field of the intent will be modified to contain the source.
+ * field of the intent will be modified to contain the source.
+ * @param source The source of the results.
+ */
+ public static void setResultsSource(Intent intent, int source) {
+ Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+ if (clipDataIntent == null) {
+ clipDataIntent = new Intent(); // First time we've added a result.
+ }
+ clipDataIntent.putExtra(EXTRA_RESULTS_SOURCE, source);
+ intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent));
+ }
+
+ /**
+ * Get the source of the RemoteInput results.
+ *
+ * @see #SOURCE_FREE_FORM_INPUT
+ * @see #SOURCE_CHOICE
+ *
+ * @param intent The intent object that fired in response to an action or content intent
+ * which also had one or more remote input requested.
+ * @return The source of the results. If no source was set, {@link #SOURCE_FREE_FORM_INPUT} will
+ * be returned.
+ */
+ public static int getResultsSource(Intent intent) {
+ Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+ if (clipDataIntent == null) {
+ return SOURCE_FREE_FORM_INPUT;
+ }
+ return clipDataIntent.getExtras().getInt(EXTRA_RESULTS_SOURCE, SOURCE_FREE_FORM_INPUT);
+ }
+
private static String getExtraResultsKeyForData(String mimeType) {
return EXTRA_DATA_TYPE_RESULTS_DATA + mimeType;
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 6eafcc4..33277ea 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -38,12 +38,12 @@
import android.content.Context;
import android.content.IRestrictionsManager;
import android.content.RestrictionsManager;
+import android.content.pm.CrossProfileApps;
+import android.content.pm.ICrossProfileApps;
import android.content.pm.IShortcutService;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
-import android.content.pm.crossprofile.CrossProfileApps;
-import android.content.pm.crossprofile.ICrossProfileApps;
import android.content.res.Resources;
import android.hardware.ConsumerIrManager;
import android.hardware.ISerialManager;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index db566e8..9f11d6e 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -79,8 +79,9 @@
* {@link BluetoothDevice} objects representing all paired devices with
* {@link #getBondedDevices()}; start device discovery with
* {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
- * listen for incoming connection requests with
- * {@link #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for
+ * listen for incoming RFComm connection requests with {@link
+ * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
+ * Channels (CoC) connection requests with listenUsingL2capCoc(int)}; or start a scan for
* Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
* </p>
* <p>This class is thread safe.</p>
@@ -210,6 +211,14 @@
public static final int STATE_BLE_TURNING_OFF = 16;
/**
+ * UUID of the GATT Read Characteristics for LE_PSM value.
+ *
+ * @hide
+ */
+ public static final UUID LE_PSM_CHARACTERISTIC_UUID =
+ UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a");
+
+ /**
* Human-readable string helper for AdapterState
*
* @hide
@@ -2160,7 +2169,9 @@
min16DigitPin);
int errno = socket.mSocket.bindListen();
if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
- socket.setChannel(socket.mSocket.getPort());
+ int assignedChannel = socket.mSocket.getPort();
+ if (DBG) Log.d(TAG, "listenUsingL2capOn: set assigned channel to " + assignedChannel);
+ socket.setChannel(assignedChannel);
}
if (errno != 0) {
//TODO(BT): Throw the same exception error code
@@ -2201,12 +2212,18 @@
* @hide
*/
public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
+ Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port);
BluetoothServerSocket socket =
new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false,
- false);
+ false);
int errno = socket.mSocket.bindListen();
if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
- socket.setChannel(socket.mSocket.getPort());
+ int assignedChannel = socket.mSocket.getPort();
+ if (DBG) {
+ Log.d(TAG, "listenUsingInsecureL2capOn: set assigned channel to "
+ + assignedChannel);
+ }
+ socket.setChannel(assignedChannel);
}
if (errno != 0) {
//TODO(BT): Throw the same exception error code
@@ -2765,4 +2782,103 @@
scanner.stopScan(scanCallback);
}
}
+
+ /**
+ * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
+ * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen
+ * for incoming connections.
+ * <p>A remote device connecting to this socket will be authenticated and communication on this
+ * socket will be encrypted.
+ * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
+ * {@link BluetoothServerSocket}.
+ * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {#link
+ * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is
+ * closed, Bluetooth is turned off, or the application exits unexpectedly.
+ * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
+ * defined and performed by the application.
+ * <p>Use {@link BluetoothDevice#createL2capCocSocket(int, int)} to connect to this server
+ * socket from another Android device that is given the PSM value.
+ *
+ * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
+ * @return an L2CAP CoC BluetoothServerSocket
+ * @throws IOException on error, for example Bluetooth not available, or insufficient
+ * permissions, or unable to start this CoC
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothServerSocket listenUsingL2capCoc(int transport)
+ throws IOException {
+ if (transport != BluetoothDevice.TRANSPORT_LE) {
+ throw new IllegalArgumentException("Unsupported transport: " + transport);
+ }
+ BluetoothServerSocket socket =
+ new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true,
+ SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
+ int errno = socket.mSocket.bindListen();
+ if (errno != 0) {
+ throw new IOException("Error: " + errno);
+ }
+
+ int assignedPsm = socket.mSocket.getPort();
+ if (assignedPsm == 0) {
+ throw new IOException("Error: Unable to assign PSM value");
+ }
+ if (DBG) {
+ Log.d(TAG, "listenUsingL2capCoc: set assigned PSM to "
+ + assignedPsm);
+ }
+ socket.setChannel(assignedPsm);
+
+ return socket;
+ }
+
+ /**
+ * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
+ * assign a dynamic PSM value. This socket can be used to listen for incoming connections.
+ * <p>The link key is not required to be authenticated, i.e the communication may be vulnerable
+ * to man-in-the-middle attacks. Use {@link #listenUsingL2capCoc}, if an encrypted and
+ * authenticated communication channel is desired.
+ * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
+ * {@link BluetoothServerSocket}.
+ * <p>The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value
+ * can be read from the {#link BluetoothServerSocket#getPsm()} and this value will be released
+ * when this server socket is closed, Bluetooth is turned off, or the application exits
+ * unexpectedly.
+ * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
+ * defined and performed by the application.
+ * <p>Use {@link BluetoothDevice#createInsecureL2capCocSocket(int, int)} to connect to this
+ * server socket from another Android device that is given the PSM value.
+ *
+ * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
+ * @return an L2CAP CoC BluetoothServerSocket
+ * @throws IOException on error, for example Bluetooth not available, or insufficient
+ * permissions, or unable to start this CoC
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport)
+ throws IOException {
+ if (transport != BluetoothDevice.TRANSPORT_LE) {
+ throw new IllegalArgumentException("Unsupported transport: " + transport);
+ }
+ BluetoothServerSocket socket =
+ new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false,
+ SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
+ int errno = socket.mSocket.bindListen();
+ if (errno != 0) {
+ throw new IOException("Error: " + errno);
+ }
+
+ int assignedPsm = socket.mSocket.getPort();
+ if (assignedPsm == 0) {
+ throw new IOException("Error: Unable to assign PSM value");
+ }
+ if (DBG) {
+ Log.d(TAG, "listenUsingInsecureL2capOn: set assigned PSM to "
+ + assignedPsm);
+ }
+ socket.setChannel(assignedPsm);
+
+ return socket;
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 9b736b7..ac21395 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1921,4 +1921,75 @@
}
return null;
}
+
+ /**
+ * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
+ * be used to start a secure outgoing connection to the remote device with the same dynamic
+ * protocol/service multiplexer (PSM) value.
+ * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capCoc(int)} for
+ * peer-peer Bluetooth applications.
+ * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
+ * <p>Application using this API is responsible for obtaining PSM value from remote device.
+ * <p>The remote device will be authenticated and communication on this socket will be
+ * encrypted.
+ * <p> Use this socket if an authenticated socket link is possible. Authentication refers
+ * to the authentication of the link key to prevent man-in-the-middle type of attacks. When a
+ * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int,
+ * int)}.
+ *
+ * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
+ * @param psm dynamic PSM value from remote device
+ * @return a CoC #BluetoothSocket ready for an outgoing connection
+ * @throws IOException on error, for example Bluetooth not available, or insufficient
+ * permissions
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException {
+ if (!isBluetoothEnabled()) {
+ Log.e(TAG, "createL2capCocSocket: Bluetooth is not enabled");
+ throw new IOException();
+ }
+ if (transport != BluetoothDevice.TRANSPORT_LE) {
+ throw new IllegalArgumentException("Unsupported transport: " + transport);
+ }
+ if (DBG) Log.d(TAG, "createL2capCocSocket: transport=" + transport + ", psm=" + psm);
+ return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm,
+ null);
+ }
+
+ /**
+ * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
+ * be used to start a secure outgoing connection to the remote device with the same dynamic
+ * protocol/service multiplexer (PSM) value.
+ * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingInsecureL2capCoc(int)}
+ * for peer-peer Bluetooth applications.
+ * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
+ * <p>Application using this API is responsible for obtaining PSM value from remote device.
+ * <p> The communication channel may not have an authenticated link key, i.e. it may be subject
+ * to man-in-the-middle attacks. Use {@link #createL2capCocSocket(int, int)} if an encrypted and
+ * authenticated communication channel is possible.
+ *
+ * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
+ * @param psm dynamic PSM value from remote device
+ * @return a CoC #BluetoothSocket ready for an outgoing connection
+ * @throws IOException on error, for example Bluetooth not available, or insufficient
+ * permissions
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException {
+ if (!isBluetoothEnabled()) {
+ Log.e(TAG, "createInsecureL2capCocSocket: Bluetooth is not enabled");
+ throw new IOException();
+ }
+ if (transport != BluetoothDevice.TRANSPORT_LE) {
+ throw new IllegalArgumentException("Unsupported transport: " + transport);
+ }
+ if (DBG) {
+ Log.d(TAG, "createInsecureL2capCocSocket: transport=" + transport + ", psm=" + psm);
+ }
+ return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
+ null);
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index 58d090d..ebb7f18 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -68,6 +68,7 @@
public final class BluetoothServerSocket implements Closeable {
private static final String TAG = "BluetoothServerSocket";
+ private static final boolean DBG = false;
/*package*/ final BluetoothSocket mSocket;
private Handler mHandler;
private int mMessage;
@@ -169,6 +170,7 @@
* close any {@link BluetoothSocket} received from {@link #accept()}.
*/
public void close() throws IOException {
+ if (DBG) Log.d(TAG, "BluetoothServerSocket:close() called. mChannel=" + mChannel);
synchronized (this) {
if (mHandler != null) {
mHandler.obtainMessage(mMessage).sendToTarget();
@@ -197,6 +199,20 @@
}
/**
+ * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
+ * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
+ * {#link BluetoothAdapter.listenUsingL2capCoc(int)} or {#link
+ * BluetoothAdapter.listenUsingInsecureL2capCoc(int)}. The returned value is undefined if this
+ * method is called on non-L2CAP server sockets.
+ *
+ * @return the assigned PSM or LE_PSM value depending on transport
+ * @hide
+ */
+ public int getPsm() {
+ return mChannel;
+ }
+
+ /**
* Sets the channel on which future sockets are bound.
* Currently used only when a channel is auto generated.
*/
@@ -227,6 +243,10 @@
sb.append("TYPE_L2CAP");
break;
}
+ case BluetoothSocket.TYPE_L2CAP_LE: {
+ sb.append("TYPE_L2CAP_LE");
+ break;
+ }
case BluetoothSocket.TYPE_SCO: {
sb.append("TYPE_SCO");
break;
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 0569913..09f9684 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -99,6 +99,16 @@
/** L2CAP socket */
public static final int TYPE_L2CAP = 3;
+ /** L2CAP socket on BR/EDR transport
+ * @hide
+ */
+ public static final int TYPE_L2CAP_BREDR = TYPE_L2CAP;
+
+ /** L2CAP socket on LE transport
+ * @hide
+ */
+ public static final int TYPE_L2CAP_LE = 4;
+
/*package*/ static final int EBADFD = 77;
/*package*/ static final int EADDRINUSE = 98;
@@ -417,6 +427,7 @@
return -1;
}
try {
+ if (DBG) Log.d(TAG, "bindListen(): mPort=" + mPort + ", mType=" + mType);
mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName,
mUuid, mPort, getSecurityFlags());
} catch (RemoteException e) {
@@ -451,7 +462,7 @@
mSocketState = SocketState.LISTENING;
}
}
- if (DBG) Log.d(TAG, "channel: " + channel);
+ if (DBG) Log.d(TAG, "bindListen(): channel=" + channel + ", mPort=" + mPort);
if (mPort <= -1) {
mPort = channel;
} // else ASSERT(mPort == channel)
@@ -515,7 +526,7 @@
/*package*/ int read(byte[] b, int offset, int length) throws IOException {
int ret = 0;
if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length);
- if (mType == TYPE_L2CAP) {
+ if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
int bytesToRead = length;
if (VDBG) {
Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length
@@ -558,7 +569,7 @@
// Rfcomm uses dynamic allocation, and should not have any bindings
// to the actual message length.
if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
- if (mType == TYPE_L2CAP) {
+ if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
if (length <= mMaxTxPacketSize) {
mSocketOS.write(b, offset, length);
} else {
@@ -702,7 +713,7 @@
}
private void createL2capRxBuffer() {
- if (mType == TYPE_L2CAP) {
+ if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
// Allocate the buffer to use for reads.
if (VDBG) Log.v(TAG, " Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f69e764..265f7c7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4146,7 +4146,7 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
- * {@link android.content.pm.crossprofile.CrossProfileApps} for cross profile operations.
+ * {@link android.content.pm.CrossProfileApps} for cross profile operations.
*
* @see #getSystemService(String)
*/
diff --git a/core/java/android/content/pm/crossprofile/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
similarity index 92%
rename from core/java/android/content/pm/crossprofile/CrossProfileApps.java
rename to core/java/android/content/pm/CrossProfileApps.java
index 414c138..7d5d609 100644
--- a/core/java/android/content/pm/crossprofile/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.content.pm.crossprofile;
+package android.content.pm;
import android.annotation.NonNull;
import android.content.ComponentName;
@@ -57,13 +57,14 @@
* action {@link android.content.Intent#ACTION_MAIN}, category
* {@link android.content.Intent#CATEGORY_LAUNCHER}. Otherwise, SecurityException will
* be thrown.
- * @param user The UserHandle of the profile, must be one of the users returned by
+ * @param targetUser The UserHandle of the profile, must be one of the users returned by
* {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
* be thrown.
*/
- public void startMainActivity(@NonNull ComponentName component, @NonNull UserHandle user) {
+ public void startMainActivity(@NonNull ComponentName component,
+ @NonNull UserHandle targetUser) {
try {
- mService.startActivityAsUser(mContext.getPackageName(), component, user);
+ mService.startActivityAsUser(mContext.getPackageName(), component, targetUser);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -114,7 +115,7 @@
}
/**
- * Return an icon that calling app can show to user for the semantic of profile switching --
+ * Return a drawable that calling app can show to user for the semantic of profile switching --
* launching its own activity in specified user profile. For example, it may return a briefcase
* icon if the given user handle is the managed profile one.
*
@@ -124,9 +125,9 @@
* @return an icon that calling app can show user for the semantic of launching its own
* activity in specified user profile.
*
- * @see #startMainActivity(ComponentName, UserHandle, Rect, Bundle)
+ * @see #startMainActivity(ComponentName, UserHandle)
*/
- public @NonNull Drawable getProfileSwitchingIcon(@NonNull UserHandle userHandle) {
+ public @NonNull Drawable getProfileSwitchingIconDrawable(@NonNull UserHandle userHandle) {
verifyCanAccessUser(userHandle);
final boolean isManagedProfile =
diff --git a/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
similarity index 95%
rename from core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl
rename to core/java/android/content/pm/ICrossProfileApps.aidl
index 227f91f5..e79deb9 100644
--- a/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.content.pm.crossprofile;
+package android.content.pm;
import android.content.ComponentName;
import android.content.Intent;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 44b4a33..bcf80ee 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1297,6 +1297,15 @@
*/
public static final int INSTALL_FAILED_INSTANT_APP_INVALID = -116;
+ /**
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the dex metadata file is invalid or
+ * if there was no matching apk file for a dex metadata file.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FAILED_BAD_DEX_METADATA = -117;
+
/** @hide */
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
DELETE_KEEP_DATA,
@@ -5618,6 +5627,8 @@
case INSTALL_FAILED_DUPLICATE_PERMISSION: return "INSTALL_FAILED_DUPLICATE_PERMISSION";
case INSTALL_FAILED_NO_MATCHING_ABIS: return "INSTALL_FAILED_NO_MATCHING_ABIS";
case INSTALL_FAILED_ABORTED: return "INSTALL_FAILED_ABORTED";
+ case INSTALL_FAILED_BAD_DEX_METADATA:
+ return "INSTALL_FAILED_BAD_DEX_METADATA";
default: return Integer.toString(status);
}
}
@@ -5662,6 +5673,7 @@
case INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: return PackageInstaller.STATUS_FAILURE_INVALID;
case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return PackageInstaller.STATUS_FAILURE_INVALID;
case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_FAILED_BAD_DEX_METADATA: return PackageInstaller.STATUS_FAILURE_INVALID;
case INSTALL_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE;
case INSTALL_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
case INSTALL_FAILED_DUPLICATE_PERMISSION: return PackageInstaller.STATUS_FAILURE_CONFLICT;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 6d6c02a..4a71467 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -240,6 +240,9 @@
}
/** @hide */
+ public static final String APK_FILE_EXTENSION = ".apk";
+
+ /** @hide */
public static class NewPermissionInfo {
public final String name;
public final int sdkVersion;
@@ -613,7 +616,7 @@
}
public static boolean isApkPath(String path) {
- return path.endsWith(".apk");
+ return path.endsWith(APK_FILE_EXTENSION);
}
/**
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
new file mode 100644
index 0000000..c5f1c85
--- /dev/null
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -0,0 +1,230 @@
+/**
+ * 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.
+ */
+
+package android.content.pm.dex;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_DEX_METADATA;
+import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
+
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageLite;
+import android.content.pm.PackageParser.PackageParserException;
+import android.util.ArrayMap;
+import android.util.jar.StrictJarFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper class used to compute and validate the location of dex metadata files.
+ *
+ * @hide
+ */
+public class DexMetadataHelper {
+ private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
+
+ private DexMetadataHelper() {}
+
+ /** Return true if the given file is a dex metadata file. */
+ public static boolean isDexMetadataFile(File file) {
+ return isDexMetadataPath(file.getName());
+ }
+
+ /** Return true if the given path is a dex metadata path. */
+ private static boolean isDexMetadataPath(String path) {
+ return path.endsWith(DEX_METADATA_FILE_EXTENSION);
+ }
+
+ /**
+ * Return the size (in bytes) of all dex metadata files associated with the given package.
+ */
+ public static long getPackageDexMetadataSize(PackageLite pkg) {
+ long sizeBytes = 0;
+ Collection<String> dexMetadataList = DexMetadataHelper.getPackageDexMetadata(pkg).values();
+ for (String dexMetadata : dexMetadataList) {
+ sizeBytes += new File(dexMetadata).length();
+ }
+ return sizeBytes;
+ }
+
+ /**
+ * Search for the dex metadata file associated with the given target file.
+ * If it exists, the method returns the dex metadata file; otherwise it returns null.
+ *
+ * Note that this performs a loose matching suitable to be used in the InstallerSession logic.
+ * i.e. the method will attempt to match the {@code dmFile} regardless of {@code targetFile}
+ * extension (e.g. 'foo.dm' will match 'foo' or 'foo.apk').
+ */
+ public static File findDexMetadataForFile(File targetFile) {
+ String dexMetadataPath = buildDexMetadataPathForFile(targetFile);
+ File dexMetadataFile = new File(dexMetadataPath);
+ return dexMetadataFile.exists() ? dexMetadataFile : null;
+ }
+
+ /**
+ * Return the dex metadata files for the given package as a map
+ * [code path -> dex metadata path].
+ *
+ * NOTE: involves I/O checks.
+ */
+ public static Map<String, String> getPackageDexMetadata(PackageParser.Package pkg) {
+ return buildPackageApkToDexMetadataMap(pkg.getAllCodePaths());
+ }
+
+ /**
+ * Return the dex metadata files for the given package as a map
+ * [code path -> dex metadata path].
+ *
+ * NOTE: involves I/O checks.
+ */
+ private static Map<String, String> getPackageDexMetadata(PackageLite pkg) {
+ return buildPackageApkToDexMetadataMap(pkg.getAllCodePaths());
+ }
+
+ /**
+ * Look up the dex metadata files for the given code paths building the map
+ * [code path -> dex metadata].
+ *
+ * For each code path (.apk) the method checks if a matching dex metadata file (.dm) exists.
+ * If it does it adds the pair to the returned map.
+ *
+ * Note that this method will do a strict
+ * matching based on the extension ('foo.dm' will only match 'foo.apk').
+ *
+ * This should only be used for code paths extracted from a package structure after the naming
+ * was enforced in the installer.
+ */
+ private static Map<String, String> buildPackageApkToDexMetadataMap(
+ List<String> codePaths) {
+ ArrayMap<String, String> result = new ArrayMap<>();
+ for (int i = codePaths.size() - 1; i >= 0; i--) {
+ String codePath = codePaths.get(i);
+ String dexMetadataPath = buildDexMetadataPathForApk(codePath);
+
+ if (Files.exists(Paths.get(dexMetadataPath))) {
+ result.put(codePath, dexMetadataPath);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Return the dex metadata path associated with the given code path.
+ * (replaces '.apk' extension with '.dm')
+ *
+ * @throws IllegalArgumentException if the code path is not an .apk.
+ */
+ public static String buildDexMetadataPathForApk(String codePath) {
+ if (!PackageParser.isApkPath(codePath)) {
+ throw new IllegalStateException(
+ "Corrupted package. Code path is not an apk " + codePath);
+ }
+ return codePath.substring(0, codePath.length() - APK_FILE_EXTENSION.length())
+ + DEX_METADATA_FILE_EXTENSION;
+ }
+
+ /**
+ * Return the dex metadata path corresponding to the given {@code targetFile} using a loose
+ * matching.
+ * i.e. the method will attempt to match the {@code dmFile} regardless of {@code targetFile}
+ * extension (e.g. 'foo.dm' will match 'foo' or 'foo.apk').
+ */
+ private static String buildDexMetadataPathForFile(File targetFile) {
+ return PackageParser.isApkFile(targetFile)
+ ? buildDexMetadataPathForApk(targetFile.getPath())
+ : targetFile.getPath() + DEX_METADATA_FILE_EXTENSION;
+ }
+
+ /**
+ * Validate the dex metadata files installed for the given package.
+ *
+ * @throws PackageParserException in case of errors.
+ */
+ public static void validatePackageDexMetadata(PackageParser.Package pkg)
+ throws PackageParserException {
+ Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values();
+ for (String dexMetadata : apkToDexMetadataList) {
+ validateDexMetadataFile(dexMetadata);
+ }
+ }
+
+ /**
+ * Validate that the given file is a dex metadata archive.
+ * This is just a sanity validation that the file is a zip archive.
+ *
+ * @throws PackageParserException if the file is not a .dm file.
+ */
+ private static void validateDexMetadataFile(String dmaPath) throws PackageParserException {
+ StrictJarFile jarFile = null;
+ try {
+ jarFile = new StrictJarFile(dmaPath, false, false);
+ } catch (IOException e) {
+ throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+ "Error opening " + dmaPath, e);
+ } finally {
+ if (jarFile != null) {
+ try {
+ jarFile.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Validates that all dex metadata paths in the given list have a matching apk.
+ * (for any foo.dm there should be either a 'foo' of a 'foo.apk' file).
+ * If that's not the case it throws {@code IllegalStateException}.
+ *
+ * This is used to perform a basic sanity check during adb install commands.
+ * (The installer does not support stand alone .dm files)
+ */
+ public static void validateDexPaths(String[] paths) {
+ ArrayList<String> apks = new ArrayList<>();
+ for (int i = 0; i < paths.length; i++) {
+ if (PackageParser.isApkPath(paths[i])) {
+ apks.add(paths[i]);
+ }
+ }
+ ArrayList<String> unmatchedDmFiles = new ArrayList<>();
+ for (int i = 0; i < paths.length; i++) {
+ String dmPath = paths[i];
+ if (isDexMetadataPath(dmPath)) {
+ boolean valid = false;
+ for (int j = apks.size() - 1; j >= 0; j--) {
+ if (dmPath.equals(buildDexMetadataPathForFile(new File(apks.get(j))))) {
+ valid = true;
+ break;
+ }
+ }
+ if (!valid) {
+ unmatchedDmFiles.add(dmPath);
+ }
+ }
+ }
+ if (!unmatchedDmFiles.isEmpty()) {
+ throw new IllegalStateException("Unmatched .dm files: " + unmatchedDmFiles);
+ }
+ }
+
+}
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 49f357e..a2991e6 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -66,7 +66,7 @@
* created or opened until one of {@link #getWritableDatabase} or
* {@link #getReadableDatabase} is called.
*
- * @param context to use to open or create the database
+ * @param context to use for locating paths to the the database
* @param name of the database file, or null for an in-memory database
* @param factory to use for creating cursor objects, or null for the default
* @param version number of the database (starting at 1); if the database is older,
@@ -86,7 +86,7 @@
* <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
* used to handle corruption when sqlite reports database corruption.</p>
*
- * @param context to use to open or create the database
+ * @param context to use for locating paths to the the database
* @param name of the database file, or null for an in-memory database
* @param factory to use for creating cursor objects, or null for the default
* @param version number of the database (starting at 1); if the database is older,
@@ -107,7 +107,7 @@
* created or opened until one of {@link #getWritableDatabase} or
* {@link #getReadableDatabase} is called.
*
- * @param context to use to open or create the database
+ * @param context to use for locating paths to the the database
* @param name of the database file, or null for an in-memory database
* @param version number of the database (starting at 1); if the database is older,
* {@link #onUpgrade} will be used to upgrade the database; if the database is
@@ -128,7 +128,7 @@
* minimumSupportedVersion is found, it is simply deleted and a new database is created with the
* given name and version
*
- * @param context to use to open or create the database
+ * @param context to use for locating paths to the the database
* @param name the name of the database file, null for a temporary in-memory database
* @param factory to use for creating cursor objects, null for default
* @param version the required version of the database
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 24e56c0..396c897 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6562,6 +6562,14 @@
public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
/**
+ * If nonzero, crashes in foreground processes will bring up a dialog.
+ * Otherwise, the process will be silently killed.
+ * @hide
+ */
+ public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION =
+ "show_first_crash_dialog_dev_option";
+
+ /**
* The {@link ComponentName} string of the service to be used as the voice recognition
* service.
*
@@ -7398,6 +7406,7 @@
SCREENSAVER_ACTIVATE_ON_DOCK,
SCREENSAVER_ACTIVATE_ON_SLEEP,
LOCKDOWN_IN_POWER_MENU,
+ SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
};
/** @hide */
@@ -11316,6 +11325,13 @@
*/
public static final String ENABLE_SMART_REPLIES_IN_NOTIFICATIONS =
"enable_smart_replies_in_notifications";
+
+ /**
+ * If nonzero, crashes in foreground processes will bring up a dialog.
+ * Otherwise, the process will be silently killed.
+ * @hide
+ */
+ public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog";
}
/**
diff --git a/core/java/android/security/keystore/RecoveryClaim.java b/core/java/android/security/keystore/RecoveryClaim.java
new file mode 100644
index 0000000..6f566af
--- /dev/null
+++ b/core/java/android/security/keystore/RecoveryClaim.java
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+/**
+ * An attempt to recover a keychain protected by remote secure hardware.
+ *
+ * @hide
+ */
+public class RecoveryClaim {
+
+ private final RecoverySession mRecoverySession;
+ private final byte[] mClaimBytes;
+
+ RecoveryClaim(RecoverySession recoverySession, byte[] claimBytes) {
+ mRecoverySession = recoverySession;
+ mClaimBytes = claimBytes;
+ }
+
+ /**
+ * Returns the session associated with the recovery attempt. This is used to match the symmetric
+ * key, which remains internal to the framework, for decrypting the claim response.
+ *
+ * @return The session data.
+ */
+ public RecoverySession getRecoverySession() {
+ return mRecoverySession;
+ }
+
+ /**
+ * Returns the encrypted claim's bytes.
+ *
+ * <p>This should be sent by the recovery agent to the remote secure hardware, which will use
+ * it to decrypt the keychain, before sending it re-encrypted with the session's symmetric key
+ * to the device.
+ */
+ public byte[] getClaimBytes() {
+ return mClaimBytes;
+ }
+}
diff --git a/core/java/android/security/keystore/RecoveryManager.java b/core/java/android/security/keystore/RecoveryController.java
similarity index 88%
rename from core/java/android/security/keystore/RecoveryManager.java
rename to core/java/android/security/keystore/RecoveryController.java
index f01ed06..87283cb 100644
--- a/core/java/android/security/keystore/RecoveryManager.java
+++ b/core/java/android/security/keystore/RecoveryController.java
@@ -23,6 +23,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.util.Log;
import com.android.internal.widget.ILockSettings;
@@ -30,12 +31,26 @@
import java.util.Map;
/**
- * A wrapper around KeyStore which lets key be exported to trusted hardware on server side and
- * recovered later.
+ * An assistant for generating {@link javax.crypto.SecretKey} instances that can be recovered by
+ * other Android devices belonging to the user. The exported keychain is protected by the user's
+ * lock screen.
+ *
+ * <p>The RecoveryController must be paired with a recovery agent. The recovery agent is responsible
+ * for transporting the keychain to remote trusted hardware. This hardware must prevent brute force
+ * attempts against the user's lock screen by limiting the number of allowed guesses (to, e.g., 10).
+ * After that number of incorrect guesses, the trusted hardware no longer allows access to the
+ * key chain.
+ *
+ * <p>For now only the recovery agent itself is able to create keys, so it is expected that the
+ * recovery agent is itself the system app.
+ *
+ * <p>A recovery agent requires the privileged permission
+ * {@code android.Manifest.permission#RECOVER_KEYSTORE}.
*
* @hide
*/
-public class RecoveryManager {
+public class RecoveryController {
+ private static final String TAG = "RecoveryController";
/** Key has been successfully synced. */
public static final int RECOVERY_STATUS_SYNCED = 0;
@@ -94,28 +109,28 @@
private final ILockSettings mBinder;
- private RecoveryManager(ILockSettings binder) {
+ private RecoveryController(ILockSettings binder) {
mBinder = binder;
}
/**
* Gets a new instance of the class.
*/
- public static RecoveryManager getInstance() {
+ public static RecoveryController getInstance() {
ILockSettings lockSettings =
ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
- return new RecoveryManager(lockSettings);
+ return new RecoveryController(lockSettings);
}
/**
- * Initializes key recovery service for the calling application. RecoveryManager
+ * Initializes key recovery service for the calling application. RecoveryController
* randomly chooses one of the keys from the list and keeps it to use for future key export
* operations. Collection of all keys in the list must be signed by the provided {@code
* rootCertificateAlias}, which must also be present in the list of root certificates
- * preinstalled on the device. The random selection allows RecoveryManager to select
+ * preinstalled on the device. The random selection allows RecoveryController to select
* which of a set of remote recovery service devices will be used.
*
- * <p>In addition, RecoveryManager enforces a delay of three months between
+ * <p>In addition, RecoveryController enforces a delay of three months between
* consecutive initialization attempts, to limit the ability of an attacker to often switch
* remote recovery devices and significantly increase number of recovery attempts.
*
@@ -371,7 +386,6 @@
* The method generates symmetric key for a session, which trusted remote device can use to
* return recovery key.
*
- * @param sessionId ID for recovery session.
* @param verifierPublicKey Encoded {@code java.security.cert.X509Certificate} with Public key
* used to create the recovery blob on the source device.
* Keystore will verify the certificate using root of trust.
@@ -380,30 +394,31 @@
* @param vaultChallenge Data passed from server for this recovery session and used to prevent
* replay attacks
* @param secrets Secrets provided by user, the method only uses type and secret fields.
- * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and contains
- * a proof of user secrets, session symmetric key and parameters necessary to identify the
- * counter with the number of failed recovery attempts.
+ * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is
+ * encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric
+ * key and parameters necessary to identify the counter with the number of failed recovery
+ * attempts.
* @throws BadCertificateFormatException if the {@code verifierPublicKey} is in an incorrect
* format.
* @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
* service.
*/
- public @NonNull byte[] startRecoverySession(
- @NonNull String sessionId,
+ @NonNull public RecoveryClaim startRecoverySession(
@NonNull byte[] verifierPublicKey,
@NonNull byte[] vaultParams,
@NonNull byte[] vaultChallenge,
@NonNull List<KeychainProtectionParams> secrets)
throws BadCertificateFormatException, InternalRecoveryServiceException {
try {
+ RecoverySession recoverySession = RecoverySession.newInstance(this);
byte[] recoveryClaim =
mBinder.startRecoverySession(
- sessionId,
+ recoverySession.getSessionId(),
verifierPublicKey,
vaultParams,
vaultChallenge,
secrets);
- return recoveryClaim;
+ return new RecoveryClaim(recoverySession, recoveryClaim);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
@@ -417,8 +432,8 @@
/**
* Imports keys.
*
- * @param sessionId Id for recovery session, same as in
- * {@link #startRecoverySession(String, byte[], byte[], byte[], List)}.
+ * @param session Related recovery session, as originally created by invoking
+ * {@link #startRecoverySession(byte[], byte[], byte[], List)}.
* @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
* @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
* and session. KeyStore only uses package names from the application info in {@link
@@ -429,14 +444,14 @@
* @throws InternalRecoveryServiceException if an error occurs internal to the recovery service.
*/
public Map<String, byte[]> recoverKeys(
- @NonNull String sessionId,
+ @NonNull RecoverySession session,
@NonNull byte[] recoveryKeyBlob,
@NonNull List<WrappedApplicationKey> applicationKeys)
throws SessionExpiredException, DecryptionFailedException,
InternalRecoveryServiceException {
try {
return (Map<String, byte[]>) mBinder.recoverKeys(
- sessionId, recoveryKeyBlob, applicationKeys);
+ session.getSessionId(), recoveryKeyBlob, applicationKeys);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
@@ -451,6 +466,20 @@
}
/**
+ * Deletes all data associated with {@code session}. Should not be invoked directly but via
+ * {@link RecoverySession#close()}.
+ *
+ * @hide
+ */
+ void closeSession(RecoverySession session) {
+ try {
+ mBinder.closeSession(session.getSessionId());
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.e(TAG, "Unexpected error trying to close session", e);
+ }
+ }
+
+ /**
* Generates a key called {@code alias} and loads it into the recoverable key store. Returns the
* raw material of the key.
*
diff --git a/core/java/android/security/keystore/RecoveryControllerException.java b/core/java/android/security/keystore/RecoveryControllerException.java
index 31fd4af..5b806b7 100644
--- a/core/java/android/security/keystore/RecoveryControllerException.java
+++ b/core/java/android/security/keystore/RecoveryControllerException.java
@@ -19,7 +19,7 @@
import java.security.GeneralSecurityException;
/**
- * Base exception for errors thrown by {@link RecoveryManager}.
+ * Base exception for errors thrown by {@link RecoveryController}.
*
* @hide
*/
diff --git a/core/java/android/security/keystore/RecoverySession.java b/core/java/android/security/keystore/RecoverySession.java
new file mode 100644
index 0000000..ae8d91a
--- /dev/null
+++ b/core/java/android/security/keystore/RecoverySession.java
@@ -0,0 +1,71 @@
+/*
+ * 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 java.security.SecureRandom;
+
+/**
+ * Session to recover a {@link KeychainSnapshot} from the remote trusted hardware, initiated by a
+ * recovery agent.
+ *
+ * @hide
+ */
+public class RecoverySession implements AutoCloseable {
+
+ private static final int SESSION_ID_LENGTH_BYTES = 16;
+
+ private final String mSessionId;
+ private final RecoveryController mRecoveryController;
+
+ private RecoverySession(RecoveryController recoveryController, String sessionId) {
+ mRecoveryController = recoveryController;
+ mSessionId = sessionId;
+ }
+
+ /**
+ * A new session, started by {@code recoveryManager}.
+ */
+ static RecoverySession newInstance(RecoveryController recoveryController) {
+ return new RecoverySession(recoveryController, newSessionId());
+ }
+
+ /**
+ * Returns a new random session ID.
+ */
+ private static String newSessionId() {
+ SecureRandom secureRandom = new SecureRandom();
+ byte[] sessionId = new byte[SESSION_ID_LENGTH_BYTES];
+ secureRandom.nextBytes(sessionId);
+ StringBuilder sb = new StringBuilder();
+ for (byte b : sessionId) {
+ sb.append(Byte.toHexString(b, /*upperCase=*/ false));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * An internal session ID, used by the framework to match recovery claims to snapshot responses.
+ */
+ String getSessionId() {
+ return mSessionId;
+ }
+
+ @Override
+ public void close() {
+ mRecoveryController.closeSession(this);
+ }
+}
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index c93e036..c7d4a4a 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -356,6 +356,7 @@
@IntRange(from = 0) int start,
@IntRange(from = 0) int end,
@NonNull TextDirectionHeuristic textDir,
+ boolean computeHyphenation,
@Nullable MeasuredParagraph recycle) {
final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
mt.resetAndAnalyzeBidi(text, start, end, textDir);
@@ -365,7 +366,8 @@
long nativeBuilderPtr = nInitBuilder();
try {
mt.bindNativeObject(
- nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer));
+ nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
+ computeHyphenation));
} finally {
nFreeBuilder(nativeBuilderPtr);
}
@@ -394,7 +396,8 @@
mt.mSpanEndCache.append(spanEnd);
}
}
- mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer));
+ mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
+ computeHyphenation));
} finally {
nFreeBuilder(nativeBuilderPtr);
}
@@ -668,7 +671,8 @@
@FloatRange(from = 0) float width);
private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
- @NonNull char[] text);
+ @NonNull char[] text,
+ boolean computeHyphenation);
private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index 2c30360..b96b489 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -52,70 +52,147 @@
// The sorted paragraph end offsets.
private final @NonNull int[] mParagraphBreakPoints;
- /**
- * Build MeasuredText from the text.
- *
- * @param text The text to be measured.
- * @param paint The paint to be used for drawing.
- * @param textDir The text direction.
- * @return The measured text.
- */
- public static @NonNull MeasuredText build(@NonNull CharSequence text,
- @NonNull TextPaint paint,
- @NonNull TextDirectionHeuristic textDir) {
- return MeasuredText.build(text, paint, textDir, 0, text.length());
- }
+ // The break strategy for this measured text.
+ private final @Layout.BreakStrategy int mBreakStrategy;
+
+ // The hyphenation frequency for this measured text.
+ private final @Layout.HyphenationFrequency int mHyphenationFrequency;
/**
- * Build MeasuredText from the specific range of the text..
- *
- * @param text The text to be measured.
- * @param paint The paint to be used for drawing.
- * @param textDir The text direction.
- * @param start The inclusive start offset of the text.
- * @param end The exclusive start offset of the text.
- * @return The measured text.
+ * A Builder for MeasuredText
*/
- public static @NonNull MeasuredText build(@NonNull CharSequence text,
- @NonNull TextPaint paint,
- @NonNull TextDirectionHeuristic textDir,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end) {
- Preconditions.checkNotNull(text);
- Preconditions.checkNotNull(paint);
- Preconditions.checkNotNull(textDir);
- Preconditions.checkArgumentInRange(start, 0, text.length(), "start");
- Preconditions.checkArgumentInRange(end, 0, text.length(), "end");
+ public static final class Builder {
+ // Mandatory parameters.
+ private final @NonNull CharSequence mText;
+ private final @NonNull TextPaint mPaint;
- final IntArray paragraphEnds = new IntArray();
- final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
+ // Members to be updated by setters.
+ private @IntRange(from = 0) int mStart;
+ private @IntRange(from = 0) int mEnd;
+ private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
+ private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
+ private @Layout.HyphenationFrequency int mHyphenationFrequency =
+ Layout.HYPHENATION_FREQUENCY_NORMAL;
- int paraEnd = 0;
- for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
- paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end);
- if (paraEnd < 0) {
- // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph end.
- paraEnd = end;
- } else {
- paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph.
- }
- paragraphEnds.add(paraEnd);
- measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
- paint, text, paraStart, paraEnd, textDir, null /* no recycle */));
+ /**
+ * Builder constructor
+ *
+ * @param text The text to be measured.
+ * @param paint The paint to be used for drawing.
+ */
+ public Builder(@NonNull CharSequence text, @NonNull TextPaint paint) {
+ Preconditions.checkNotNull(text);
+ Preconditions.checkNotNull(paint);
+
+ mText = text;
+ mPaint = paint;
+ mStart = 0;
+ mEnd = text.length();
}
- return new MeasuredText(text, start, end, paint, textDir,
- measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]),
- paragraphEnds.toArray());
- }
+ /**
+ * Set the range of measuring target.
+ *
+ * @param start The measuring target start offset in the text.
+ * @param end The measuring target end offset in the text.
+ */
+ public @NonNull Builder setRange(@IntRange(from = 0) int start,
+ @IntRange(from = 0) int end) {
+ Preconditions.checkArgumentInRange(start, 0, mText.length(), "start");
+ Preconditions.checkArgumentInRange(end, 0, mText.length(), "end");
+ Preconditions.checkArgument(start <= end, "The range is reversed.");
- // Use MeasuredText.build instead.
+ mStart = start;
+ mEnd = end;
+ return this;
+ }
+
+ /**
+ * Set the text direction heuristic
+ *
+ * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
+ *
+ * @param textDir The text direction heuristic for resolving bidi behavior.
+ * @return this builder, useful for chaining.
+ */
+ public @NonNull Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
+ Preconditions.checkNotNull(textDir);
+ mTextDir = textDir;
+ return this;
+ }
+
+ /**
+ * Set the break strategy
+ *
+ * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
+ *
+ * @param breakStrategy The break strategy.
+ * @return this builder, useful for chaining.
+ */
+ public @NonNull Builder setBreakStrategy(@Layout.BreakStrategy int breakStrategy) {
+ mBreakStrategy = breakStrategy;
+ return this;
+ }
+
+ /**
+ * Set the hyphenation frequency
+ *
+ * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
+ *
+ * @param hyphenationFrequency The hyphenation frequency.
+ * @return this builder, useful for chaining.
+ */
+ public @NonNull Builder setHyphenationFrequency(
+ @Layout.HyphenationFrequency int hyphenationFrequency) {
+ mHyphenationFrequency = hyphenationFrequency;
+ return this;
+ }
+
+ /**
+ * Build the measured text
+ *
+ * @return the measured text.
+ */
+ public @NonNull MeasuredText build() {
+ final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE
+ && mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE;
+
+ final IntArray paragraphEnds = new IntArray();
+ final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
+
+ int paraEnd = 0;
+ for (int paraStart = mStart; paraStart < mEnd; paraStart = paraEnd) {
+ paraEnd = TextUtils.indexOf(mText, LINE_FEED, paraStart, mEnd);
+ if (paraEnd < 0) {
+ // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph
+ // end.
+ paraEnd = mEnd;
+ } else {
+ paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph.
+ }
+
+ paragraphEnds.add(paraEnd);
+ measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
+ mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation,
+ null /* no recycle */));
+ }
+
+ return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy,
+ mHyphenationFrequency, measuredTexts.toArray(
+ new MeasuredParagraph[measuredTexts.size()]),
+ paragraphEnds.toArray());
+ }
+ };
+
+ // Use MeasuredText.Builder instead.
private MeasuredText(@NonNull CharSequence text,
@IntRange(from = 0) int start,
@IntRange(from = 0) int end,
@NonNull TextPaint paint,
@NonNull TextDirectionHeuristic textDir,
+ @Layout.BreakStrategy int breakStrategy,
+ @Layout.HyphenationFrequency int frequency,
@NonNull MeasuredParagraph[] measuredTexts,
@NonNull int[] paragraphBreakPoints) {
mText = text;
@@ -125,6 +202,8 @@
mMeasuredParagraphs = measuredTexts;
mParagraphBreakPoints = paragraphBreakPoints;
mTextDir = textDir;
+ mBreakStrategy = breakStrategy;
+ mHyphenationFrequency = frequency;
}
/**
@@ -190,6 +269,20 @@
return mMeasuredParagraphs[paraIndex];
}
+ /**
+ * Returns the break strategy for this text.
+ */
+ public @Layout.BreakStrategy int getBreakStrategy() {
+ return mBreakStrategy;
+ }
+
+ /**
+ * Returns the hyphenation frequency for this text.
+ */
+ public @Layout.HyphenationFrequency int getHyphenationFrequency() {
+ return mHyphenationFrequency;
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////
// Spanned overrides
//
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 36bec86..70d6486 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -653,26 +653,40 @@
MeasuredText measured = null;
final Spanned spanned;
+ final boolean canUseMeasuredText;
if (source instanceof MeasuredText) {
measured = (MeasuredText) source;
- final CharSequence original = measured.getText();
- spanned = (original instanceof Spanned) ? (Spanned) original : null;
-
if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) {
// The buffer position has changed. Re-measure here.
- measured = MeasuredText.build(original, paint, textDir, bufStart, bufEnd);
+ canUseMeasuredText = false;
+ } else if (b.mBreakStrategy != measured.getBreakStrategy()
+ || b.mHyphenationFrequency != measured.getHyphenationFrequency()) {
+ // The computed hyphenation pieces may not be able to used. Re-measure it.
+ canUseMeasuredText = false;
} else {
// We can use measured information.
-
- // Overwrite with the one when emeasured.
- // TODO: Give an option for developer not to overwrite and measure again here?
- textDir = measured.getTextDir();
- paint = measured.getPaint();
+ canUseMeasuredText = true;
}
} else {
- measured = MeasuredText.build(source, paint, textDir, bufStart, bufEnd);
+ canUseMeasuredText = false;
+ }
+
+ if (!canUseMeasuredText) {
+ measured = new MeasuredText.Builder(source, paint)
+ .setRange(bufStart, bufEnd)
+ .setTextDirection(textDir)
+ .setBreakStrategy(b.mBreakStrategy)
+ .setHyphenationFrequency(b.mHyphenationFrequency)
+ .build();
spanned = (source instanceof Spanned) ? (Spanned) source : null;
+ } else {
+ final CharSequence original = measured.getText();
+ spanned = (original instanceof Spanned) ? (Spanned) original : null;
+ // Overwrite with the one when measured.
+ // TODO: Give an option for developer not to overwrite and measure again here?
+ textDir = measured.getTextDir();
+ paint = measured.getPaint();
}
try {
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 336c20c..728824c 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -106,6 +106,10 @@
@Override
public Editable getText() {
CharSequence text = super.getText();
+ // This can only happen during construction.
+ if (text == null) {
+ return null;
+ }
if (text instanceof Editable) {
return (Editable) super.getText();
}
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index e765ab1..8a456d1 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -25,6 +25,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser.PackageLite;
+import android.content.pm.dex.DexMetadataHelper;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
@@ -415,6 +416,9 @@
sizeBytes += codeFile.length();
}
+ // Include raw dex metadata files
+ sizeBytes += DexMetadataHelper.getPackageDexMetadataSize(pkg);
+
// Include all relevant native code
sizeBytes += NativeLibraryHelper.sumNativeBinariesWithOverride(handle, abiOverride);
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 6ed8973..e3f1f47 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -60,7 +60,7 @@
in byte[] token, int requestedQuality, int userId);
void unlockUserWithToken(long tokenHandle, in byte[] token, int userId);
- // Keystore RecoveryManager methods.
+ // Keystore RecoveryController methods.
// {@code ServiceSpecificException} may be thrown to signal an error, which caller can
// convert to {@code RecoveryManagerException}.
void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList);
@@ -81,4 +81,5 @@
in List<KeychainProtectionParams> secrets);
Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
in List<WrappedApplicationKey> applicationKeys);
+ void closeSession(in String sessionId);
}
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index ed9d0e9..1c2528f 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -393,7 +393,7 @@
canvas.drawBitmap(bm, 0.0f, 0.0f, &paint);
bm.swap(scaledBm);
- nativeBitmap = scaledPixelRef;
+ nativeBitmap = std::move(scaledPixelRef);
}
if (jpostProcess) {
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index 1676d4b..f3aeb32 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -27,6 +27,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
#include <ETC1/etc1.h>
#include <SkBitmap.h>
@@ -641,7 +642,7 @@
}
break;
case kRGBA_F16_SkColorType:
- if (type == GL_HALF_FLOAT_OES && format == PIXEL_FORMAT_RGBA_FP16)
+ if (type == GL_HALF_FLOAT && format == GL_RGBA16F)
return 0;
break;
default:
@@ -662,7 +663,7 @@
case kRGB_565_SkColorType:
return GL_RGB;
case kRGBA_F16_SkColorType:
- return PIXEL_FORMAT_RGBA_FP16;
+ return GL_RGBA16F;
default:
return -1;
}
@@ -680,7 +681,7 @@
case kRGB_565_SkColorType:
return GL_UNSIGNED_SHORT_5_6_5;
case kRGBA_F16_SkColorType:
- return GL_HALF_FLOAT_OES;
+ return GL_HALF_FLOAT;
default:
return -1;
}
diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp
index bdae0b2..58c05b4 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -85,12 +85,12 @@
// Regular JNI
static jlong nBuildNativeMeasuredParagraph(JNIEnv* env, jclass /* unused */, jlong builderPtr,
- jcharArray javaText) {
+ jcharArray javaText, jboolean computeHyphenation) {
ScopedCharArrayRO text(env, javaText);
const minikin::U16StringPiece textBuffer(text.get(), text.size());
// Pass the ownership to Java.
- return toJLong(toBuilder(builderPtr)->build(textBuffer).release());
+ return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation).release());
}
// Regular JNI
@@ -108,7 +108,7 @@
{"nInitBuilder", "()J", (void*) nInitBuilder},
{"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
{"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
- {"nBuildNativeMeasuredParagraph", "(J[C)J", (void*) nBuildNativeMeasuredParagraph},
+ {"nBuildNativeMeasuredParagraph", "(J[CZ)J", (void*) nBuildNativeMeasuredParagraph},
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
// MeasuredParagraph native functions.
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 3ffb254..8d6df12 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -390,8 +390,9 @@
optional SettingProto enable_gnss_raw_meas_full_tracking = 346;
optional SettingProto zram_enabled = 347;
optional SettingProto enable_smart_replies_in_notifications = 348;
+ optional SettingProto show_first_crash_dialog = 349;
- // Next tag = 349;
+ // Next tag = 350;
}
message SecureSettingsProto {
@@ -593,8 +594,9 @@
optional SettingProto qs_auto_added_tiles = 193;
optional SettingProto lockdown_in_power_menu = 194;
optional SettingProto backup_manager_constants = 169;
+ optional SettingProto show_first_crash_dialog_dev_option = 195;
- // Next tag = 195
+ // Next tag = 196
}
message SystemSettingsProto {
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index f6752c2..a7736e7c 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -10,7 +10,7 @@
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
+ See the License for the specific language gning permissions and
limitations under the License.
-->
@@ -147,6 +147,7 @@
an activity that looks like a Dialog.-->
<style name="Theme.DeviceDefault.Dialog" parent="Theme.Material.Dialog" >
<item name="windowIsFloating">false</item>
+ <item name="windowElevation">0dp</item>
<item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault</item>
<item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
@@ -183,6 +184,7 @@
<style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
<item name="windowIsFloating">false</item>
+ <item name="windowElevation">0dp</item>
<!-- Color palette Dark -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
@@ -200,6 +202,7 @@
<style name="Theme.DeviceDefault.Settings.CompactMenu" parent="Theme.Material.CompactMenu">
<item name="windowIsFloating">false</item>
+ <item name="windowElevation">0dp</item>
<!-- Color palette Dark -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
@@ -219,6 +222,7 @@
regular dialog. -->
<style name="Theme.DeviceDefault.Dialog.MinWidth" parent="Theme.Material.Dialog.MinWidth">
<item name="windowIsFloating">false</item>
+ <item name="windowElevation">0dp</item>
<!-- Color palette Dark -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
@@ -237,6 +241,7 @@
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
<style name="Theme.DeviceDefault.Dialog.NoActionBar" parent="Theme.Material.Dialog.NoActionBar">
<item name="windowIsFloating">false</item>
+ <item name="windowElevation">0dp</item>
<!-- Color palette Dark -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
@@ -256,6 +261,7 @@
for a regular dialog. -->
<style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Dialog.NoActionBar.MinWidth">
<item name="windowIsFloating">false</item>
+ <item name="windowElevation">0dp</item>
<!-- Color palette Dark -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
@@ -434,6 +440,7 @@
<style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
<item name="windowIsFloating">false</item>
+ <item name="windowElevation">0dp</item>
<!-- Color palette Dialog -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
diff --git a/core/res/res/values-watch/themes_material.xml b/core/res/res/values-watch/themes_material.xml
index 0cf398b..40a249a 100644
--- a/core/res/res/values-watch/themes_material.xml
+++ b/core/res/res/values-watch/themes_material.xml
@@ -43,6 +43,7 @@
<!-- Override behaviour to set the theme colours for dialogs, keep them the same. -->
<style name="ThemeOverlay.Material.Dialog" parent="ThemeOverlay.Material.BaseDialog">
<item name="windowIsFloating">false</item>
+ <item name="windowElevation">0dp</item>
</style>
<!-- Force the background and floating colours to be the default colours. -->
@@ -51,6 +52,7 @@
<item name="colorBackgroundFloating">@color/background_floating_material_dark</item>
<item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
<item name="windowIsFloating">false</item>
+ <item name="windowElevation">0dp</item>
</style>
<!-- Force the background and floating colours to be the default colours. -->
@@ -59,6 +61,7 @@
<item name="colorBackgroundFloating">@color/background_floating_material_light</item>
<item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
<item name="windowIsFloating">false</item>
+ <item name="windowElevation">0dp</item>
</style>
<!-- Force all settings themes to use normal Material theme. -->
diff --git a/core/tests/coretests/apks/install-split-base/Android.mk b/core/tests/coretests/apks/install-split-base/Android.mk
new file mode 100644
index 0000000..5b60e31
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-base/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := install_split_base
+
+include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/install-split-base/AndroidManifest.xml b/core/tests/coretests/apks/install-split-base/AndroidManifest.xml
new file mode 100644
index 0000000..c2bfedd
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-base/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_split"
+ android:isolatedSplits="true">
+
+ <application android:label="ClassloaderSplitApp">
+ <activity android:name=".BaseActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install-split-base/src/com/google/android/dexapis/splitapp/BaseActivity.java b/core/tests/coretests/apks/install-split-base/src/com/google/android/dexapis/splitapp/BaseActivity.java
new file mode 100644
index 0000000..cb5760ce
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-base/src/com/google/android/dexapis/splitapp/BaseActivity.java
@@ -0,0 +1,23 @@
+/**
+ * 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.
+ */
+
+package com.google.android.dexapis.splitapp;
+
+import android.app.Activity;
+
+/** Main activity */
+public class BaseActivity extends Activity {
+}
diff --git a/core/tests/coretests/apks/install-split-feature-a/Android.mk b/core/tests/coretests/apks/install-split-feature-a/Android.mk
new file mode 100644
index 0000000..0f37d16
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-feature-a/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := install_split_feature_a
+
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_FLAGS += --custom-package com.google.android.dexapis.splitapp.feature_a
+LOCAL_AAPT_FLAGS += --package-id 0x80
+
+include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/install-split-feature-a/AndroidManifest.xml b/core/tests/coretests/apks/install-split-feature-a/AndroidManifest.xml
new file mode 100644
index 0000000..3221c75
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-feature-a/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_split"
+ featureSplit="feature_a">
+
+ <application>
+ <activity android:name=".feature_a.FeatureAActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install-split-feature-a/src/com/google/android/dexapis/splitapp/feature_a/FeatureAActivity.java b/core/tests/coretests/apks/install-split-feature-a/src/com/google/android/dexapis/splitapp/feature_a/FeatureAActivity.java
new file mode 100644
index 0000000..0af5f89
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-feature-a/src/com/google/android/dexapis/splitapp/feature_a/FeatureAActivity.java
@@ -0,0 +1,23 @@
+/**
+ * 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.
+ */
+
+package com.google.android.dexapis.splitapp.feature_a;
+
+import android.app.Activity;
+
+/** Main activity */
+public class FeatureAActivity extends Activity {
+}
diff --git a/core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
similarity index 92%
rename from core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java
rename to core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
index 80e4c02..0cfcd8f8 100644
--- a/core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java
+++ b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.content.pm.crossprofile;
+package android.content.pm;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.nullable;
@@ -42,7 +42,7 @@
/**
* Build/Install/Run:
- * bit FrameworksCoreTests:android.content.pm.crossprofile.CrossProfileAppsTest
+ * atest frameworks/base/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
*/
@Presubmit
@RunWith(MockitoJUnitRunner.class)
@@ -118,7 +118,7 @@
public void getProfileSwitchingIcon_managedProfile() {
setValidTargetProfile(MANAGED_PROFILE);
- mCrossProfileApps.getProfileSwitchingIcon(MANAGED_PROFILE);
+ mCrossProfileApps.getProfileSwitchingIconDrawable(MANAGED_PROFILE);
verify(mResources).getDrawable(R.drawable.ic_corp_badge, null);
}
@@ -126,13 +126,13 @@
public void getProfileSwitchingIcon_personalProfile() {
setValidTargetProfile(PERSONAL_PROFILE);
- mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE);
+ mCrossProfileApps.getProfileSwitchingIconDrawable(PERSONAL_PROFILE);
verify(mResources).getDrawable(R.drawable.ic_account_circle, null);
}
@Test(expected = SecurityException.class)
public void getProfileSwitchingIcon_securityException() {
- mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE);
+ mCrossProfileApps.getProfileSwitchingIconDrawable(PERSONAL_PROFILE);
}
private void setValidTargetProfile(UserHandle userHandle) {
diff --git a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
new file mode 100644
index 0000000..4b84429
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
@@ -0,0 +1,208 @@
+/**
+ * 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.pm.dex;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.Package;
+import android.content.pm.PackageParser.PackageParserException;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.frameworks.coretests.R;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import libcore.io.IoUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DexMetadataHelperTest {
+ private static final String APK_FILE_EXTENSION = ".apk";
+ private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
+
+ private File mTmpDir = null;
+
+ @Before
+ public void setUp() {
+ mTmpDir = IoUtils.createTemporaryDirectory("DexMetadataHelperTest");
+ }
+
+ @After
+ public void tearDown() {
+ if (mTmpDir != null) {
+ File[] files = mTmpDir.listFiles();
+ for (File f : files) {
+ f.delete();
+ }
+ }
+ }
+
+ private File createDexMetadataFile(String apkFileName) throws IOException {
+ File dmFile = new File(mTmpDir, apkFileName.replace(APK_FILE_EXTENSION,
+ DEX_METADATA_FILE_EXTENSION));
+ try (FileOutputStream fos = new FileOutputStream(dmFile)) {
+ try (ZipOutputStream zipOs = new ZipOutputStream(fos)) {
+ zipOs.putNextEntry(new ZipEntry("primary.prof"));
+ zipOs.closeEntry();
+ }
+ }
+ return dmFile;
+ }
+
+ private File copyApkToToTmpDir(String apkFileName, int apkResourceId) throws IOException {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ File outFile = new File(mTmpDir, apkFileName);
+ try (InputStream is = context.getResources().openRawResource(apkResourceId)) {
+ FileUtils.copyToFileOrThrow(is, outFile);
+ }
+ return outFile;
+ }
+
+ @Test
+ public void testParsePackageWithDmFileValid() throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ createDexMetadataFile("install_split_base.apk");
+ Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+
+ Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
+ assertEquals(1, packageDexMetadata.size());
+ String baseDexMetadata = packageDexMetadata.get(pkg.baseCodePath);
+ assertNotNull(baseDexMetadata);
+ assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.baseCodePath));
+ }
+
+ @Test
+ public void testParsePackageSplitsWithDmFileValid()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
+ createDexMetadataFile("install_split_base.apk");
+ createDexMetadataFile("install_split_feature_a.apk");
+ Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+
+ Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
+ assertEquals(2, packageDexMetadata.size());
+ String baseDexMetadata = packageDexMetadata.get(pkg.baseCodePath);
+ assertNotNull(baseDexMetadata);
+ assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.baseCodePath));
+
+ String splitDexMetadata = packageDexMetadata.get(pkg.splitCodePaths[0]);
+ assertNotNull(splitDexMetadata);
+ assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.splitCodePaths[0]));
+ }
+
+ @Test
+ public void testParsePackageSplitsNoBaseWithDmFileValid()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
+ createDexMetadataFile("install_split_feature_a.apk");
+ Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+
+ Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
+ assertEquals(1, packageDexMetadata.size());
+
+ String splitDexMetadata = packageDexMetadata.get(pkg.splitCodePaths[0]);
+ assertNotNull(splitDexMetadata);
+ assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.splitCodePaths[0]));
+ }
+
+ @Test
+ public void testParsePackageWithDmFileInvalid() throws IOException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ File invalidDmFile = new File(mTmpDir, "install_split_base.dm");
+ Files.createFile(invalidDmFile.toPath());
+ try {
+ PackageParser.Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+ DexMetadataHelper.validatePackageDexMetadata(pkg);
+ } catch (PackageParserException e) {
+ assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
+ }
+ }
+
+ @Test
+ public void testParsePackageSplitsWithDmFileInvalid()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
+ createDexMetadataFile("install_split_base.apk");
+ File invalidDmFile = new File(mTmpDir, "install_split_feature_a.dm");
+ Files.createFile(invalidDmFile.toPath());
+
+ try {
+ PackageParser.Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+ DexMetadataHelper.validatePackageDexMetadata(pkg);
+ } catch (PackageParserException e) {
+ assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
+ }
+ }
+
+ @Test
+ public void testPackageWithDmFileNoMatch() throws IOException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ createDexMetadataFile("non_existent.apk");
+
+ try {
+ DexMetadataHelper.validateDexPaths(mTmpDir.list());
+ fail("Should fail validation");
+ } catch (IllegalStateException e) {
+ // expected.
+ }
+ }
+
+ @Test
+ public void testPackageSplitsWithDmFileNoMatch()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
+ createDexMetadataFile("install_split_base.apk");
+ createDexMetadataFile("install_split_feature_a.mistake.apk");
+
+ try {
+ DexMetadataHelper.validateDexPaths(mTmpDir.list());
+ fail("Should fail validation");
+ } catch (IllegalStateException e) {
+ // expected.
+ }
+ }
+
+ private static boolean isDexMetadataForApk(String dmaPath, String apkPath) {
+ return apkPath.substring(0, apkPath.length() - APK_FILE_EXTENSION.length()).equals(
+ dmaPath.substring(0, dmaPath.length() - DEX_METADATA_FILE_EXTENSION.length()));
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index d5689c7..c2ae776 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -331,6 +331,7 @@
Settings.Global.SETUP_PREPAID_DETECTION_REDIR_HOST,
Settings.Global.SETUP_PREPAID_DETECTION_TARGET_URL,
Settings.Global.SHORTCUT_MANAGER_CONSTANTS,
+ Settings.Global.SHOW_FIRST_CRASH_DIALOG,
Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
Settings.Global.SHOW_TEMPERATURE_WARNING,
Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index 5d33397..f6300ee 100644
--- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -132,7 +132,7 @@
public void buildForStaticLayout() {
MeasuredParagraph mt = null;
- mt = MeasuredParagraph.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, null);
+ mt = MeasuredParagraph.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, false, null);
assertNotNull(mt);
assertNotNull(mt.getChars());
assertEquals("XXX", charsToString(mt.getChars()));
@@ -147,7 +147,7 @@
// Recycle it
MeasuredParagraph mt2 =
- MeasuredParagraph.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, mt);
+ MeasuredParagraph.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, false, mt);
assertEquals(mt2, mt);
assertNotNull(mt2.getChars());
assertEquals("VVV", charsToString(mt.getChars()));
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index f169f22..993bae1 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -194,6 +194,10 @@
<allow-in-power-save package="com.android.cellbroadcastreceiver" />
<allow-in-power-save package="com.android.shell" />
+ <!-- Whitelist system providers -->
+ <allow-in-power-save-except-idle package="com.android.providers.calendar" />
+ <allow-in-power-save-except-idle package="com.android.providers.contacts" />
+
<!-- These are the packages that are white-listed to be able to run as system user -->
<system-user-whitelisted-app package="com.android.settings" />
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index e0289f0..d7861e3 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -180,6 +180,7 @@
/**
* IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES
* if applicable, as well as audioattributes.proto.
+ * Also consider adding them to <aaudio/AAudio.h> for the NDK.
*/
/**
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index d130a61..b13de2e 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -498,8 +498,6 @@
<string name="wifi_display_certification">Wireless display certification</string>
<!-- Setting Checkbox title whether to enable WiFi Verbose Logging. [CHAR LIMIT=40] -->
<string name="wifi_verbose_logging">Enable Wi\u2011Fi Verbose Logging</string>
- <!-- Setting Checkbox title whether to enable WiFi Scanning in the presence of traffic. [CHAR LIMIT=80] -->
- <string name="wifi_allow_scan_with_traffic">Always allow Wi\u2011Fi Roam Scans</string>
<!-- Setting Checkbox title whether to always keep mobile data active. [CHAR LIMIT=80] -->
<string name="mobile_data_always_on">Mobile data always active</string>
<!-- Setting Checkbox title whether to enable hardware acceleration for tethering. [CHAR LIMIT=80] -->
@@ -554,8 +552,6 @@
<string name="wifi_display_certification_summary">Show options for wireless display certification</string>
<!-- Setting Checkbox summary whether to enable Wifi verbose Logging [CHAR LIMIT=80] -->
<string name="wifi_verbose_logging_summary">Increase Wi\u2011Fi logging level, show per SSID RSSI in Wi\u2011Fi Picker</string>
- <!-- Setting Checkbox summary whether to always allow WiFi Roam Scans [CHAR LIMIT=130] -->
- <string name="wifi_allow_scan_with_traffic_summary">Allow/Disallow Wi\u2011Fi Roam Scans based on the amount of data traffic present at the interface</string>
<!-- UI debug setting: limit size of Android logger buffers -->
<string name="select_logd_size_title">Logger buffer sizes</string>
<!-- UI debug setting: limit size of Android logger buffers [CHAR LIMIT=59] -->
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index d33e084..87ed7eb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1125,6 +1125,9 @@
dumpSetting(s, p,
Settings.Global.ENABLE_SMART_REPLIES_IN_NOTIFICATIONS,
GlobalSettingsProto.ENABLE_SMART_REPLIES_IN_NOTIFICATIONS);
+ dumpSetting(s, p,
+ Settings.Global.SHOW_FIRST_CRASH_DIALOG,
+ GlobalSettingsProto.SHOW_FIRST_CRASH_DIALOG);
}
/** Dump a single {@link SettingsState.Setting} to a proto buf */
@@ -1516,6 +1519,9 @@
Settings.Secure.ANR_SHOW_BACKGROUND,
SecureSettingsProto.ANR_SHOW_BACKGROUND);
dumpSetting(s, p,
+ Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
+ SecureSettingsProto.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION);
+ dumpSetting(s, p,
Settings.Secure.VOICE_RECOGNITION_SERVICE,
SecureSettingsProto.VOICE_RECOGNITION_SERVICE);
dumpSetting(s, p,
diff --git a/packages/SystemUI/res/drawable/ic_landscape_from_auto_rotate_animation.xml b/packages/SystemUI/res/drawable/ic_landscape_from_auto_rotate_animation.xml
deleted file mode 100644
index 00bcaf6..0000000
--- a/packages/SystemUI/res/drawable/ic_landscape_from_auto_rotate_animation.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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.
--->
-<animated-vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_landscape_from_auto_rotate" >
- <target
- android:name="landscape"
- android:animation="@anim/ic_rotate_to_landscape_landscape_animation" />
- <target
- android:name="arrows"
- android:animation="@anim/ic_rotate_to_landscape_arrows_animation" />
- <target
- android:name="arrows_0"
- android:animation="@anim/ic_rotate_to_landscape_arrows_0_animation" />
- <target
- android:name="bottom_merged"
- android:animation="@anim/ic_rotate_to_landscape_bottom_merged_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate.xml
deleted file mode 100644
index cde6797..0000000
--- a/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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:name="ic_landscape_to_rotate"
- android:height="48dp"
- android:width="48dp"
- android:viewportHeight="48"
- android:viewportWidth="48"
- android:tint="?android:attr/colorControlNormal" >
- <group
- android:name="device"
- android:translateX="24"
- android:translateY="24" >
- <group
- android:name="device_pivot"
- android:translateX="-24.15"
- android:translateY="-24.25" >
- <group
- android:name="landscape"
- android:translateX="24"
- android:translateY="24" >
- <path
- android:name="device_merged"
- android:pathData="M -21.9799957275,-10.0 c 0.0,0.0 -0.0200042724609,20.0 -0.0200042724609,20.0 c 0.0,2.19999694824 1.80000305176,4.0 4.0,4.0 c 0.0,0.0 36.0,0.0 36.0,0.0 c 2.19999694824,0.0 4.0,-1.80000305176 4.0,-4.0 c 0.0,0.0 0.0,-20.0 0.0,-20.0 c 0.0,-2.19999694824 -1.80000305176,-4.0 -4.0,-4.0 c 0.0,0.0 -36.0,0.0 -36.0,0.0 c -2.19999694824,0.0 -3.97999572754,1.80000305176 -3.97999572754,4.0 Z M 14.0,10.0 c 0.0,0.0 -28.0,0.0 -28.0,0.0 c 0.0,0.0 0.0,-20.0 0.0,-20.0 c 0.0,0.0 28.0,0.0 28.0,0.0 c 0.0,0.0 0.0,20.0 0.0,20.0 Z"
- android:fillColor="#FFFFFFFF" />
- </group>
- </group>
- </group>
- <group
- android:name="arrows"
- android:translateX="24"
- android:translateY="24"
- android:rotation="-90" >
- <group
- android:name="arrows_pivot"
- android:translateX="-24.0798"
- android:translateY="-24.23" >
- <group
- android:name="arrows_0"
- android:translateX="12.2505"
- android:translateY="37.2145" >
- <path
- android:name="bottom_merged"
- android:pathData="M 20.7395019531,-31.9844970703 c 6.23999023438,2.83999633789 10.6999969482,8.7200012207 11.8399963379,15.7799987793 c 0.119995117188,0.699996948242 0.740005493164,1.2200012207 1.46099853516,1.2200012207 c 0.919998168945,0.0 1.6190032959,-0.84001159668 1.47900390625,-1.74000549316 c -1.75900268555,-10.3800048828 -9.75900268555,-19.1199951172 -22.5800018311,-20.1600036621 c -0.919998168945,-0.0800018310547 -1.43899536133,1.04000854492 -0.800003051758,1.70001220703 c 0.0,0.0 5.12100219727,5.11999511719 5.12100219727,5.11999511719 c 0.378997802734,0.380004882812 0.97900390625,0.380004882812 1.37899780273,0.020004272461 c 0.0,0.0 2.10000610352,-1.94000244141 2.10000610352,-1.94000244141 Z M 2.73950195312,6.01550292969 c -6.26000976562,-2.83999633789 -10.7200012207,-8.76000976562 -11.8399963379,-15.8600006104 c -0.118011474609,-0.667007446289 -0.702011108398,-1.15100097656 -1.38000488281,-1.13999938965 c -0.860000610352,0.0 -1.52000427246,0.759994506836 -1.38000488281,1.61999511719 c 1.54000854492,10.4000091553 9.5,19.2200012207 22.4199981689,20.2799987793 c 0.920013427734,0.0800018310547 1.44100952148,-1.03999328613 0.800003051758,-1.69999694824 c 0.0,0.0 -5.11999511719,-5.11999511719 -5.11999511719,-5.11999511719 c -0.380004882812,-0.376007080078 -0.988998413086,-0.385009765625 -1.38000488281,-0.0200042724609 c 0.0,0.0 -2.11999511719,1.94000244141 -2.11999511719,1.94000244141 Z"
- android:fillColor="#FFFFFFFF"
- android:fillAlpha="0" />
- </group>
- </group>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate_animation.xml b/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate_animation.xml
deleted file mode 100644
index 86dc6ce..0000000
--- a/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate_animation.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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.
--->
-<animated-vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_landscape_to_auto_rotate" >
- <target
- android:name="landscape"
- android:animation="@anim/ic_landscape_to_rotate_landscape_animation" />
- <target
- android:name="arrows"
- android:animation="@anim/ic_landscape_to_rotate_arrows_animation" />
- <target
- android:name="bottom_merged"
- android:animation="@anim/ic_landscape_to_rotate_bottom_merged_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate_animation.xml b/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate_animation.xml
deleted file mode 100644
index b8465f4..0000000
--- a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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.
--->
-<animated-vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_portrait_to_auto_rotate" >
- <target
- android:name="device_0"
- android:animation="@anim/ic_portrait_to_rotate_device_0_animation" />
- <target
- android:name="device_merged"
- android:animation="@anim/ic_portrait_to_rotate_device_merged_animation" />
- <target
- android:name="arrows"
- android:animation="@anim/ic_portrait_to_rotate_arrows_animation" />
- <target
- android:name="arrows_0"
- android:animation="@anim/ic_portrait_to_rotate_arrows_0_animation" />
- <target
- android:name="bottom_merged"
- android:animation="@anim/ic_portrait_to_rotate_bottom_merged_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml
deleted file mode 100644
index 0ef15f0..0000000
--- a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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:name="ic_rotate_to_portrait"
- android:height="48dp"
- android:width="48dp"
- android:viewportHeight="48"
- android:viewportWidth="48"
- android:tint="?android:attr/colorControlNormal" >
- <group
- android:name="device"
- android:translateX="24"
- android:translateY="24" >
- <group
- android:name="device_pivot"
- android:translateX="-24.15"
- android:translateY="-24.25" >
- <group
- android:name="device_0"
- android:translateX="24.14999"
- android:translateY="24.25"
- android:rotation="-135" >
- <path
- android:name="device_merged"
- android:pathData="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
- android:fillColor="#FFFFFFFF" />
- </group>
- </group>
- </group>
- <group
- android:name="arrows"
- android:translateX="24"
- android:translateY="24"
- android:rotation="-221" >
- <group
- android:name="arrows_pivot"
- android:translateX="-24.0798"
- android:translateY="-24.23" >
- <group
- android:name="arrows_0"
- android:translateX="12.2505"
- android:translateY="37.2145"
- android:scaleX="0.9"
- android:scaleY="0.9" >
- <path
- android:name="bottom_merged"
- android:pathData="M 20.7395019531,-31.9844970703 c 6.23999023438,2.83999633789 10.6999969482,8.7200012207 11.8399963379,15.7799987793 c 0.119995117188,0.699996948242 0.740005493164,1.2200012207 1.46099853516,1.2200012207 c 0.919998168945,0.0 1.6190032959,-0.84001159668 1.47900390625,-1.74000549316 c -1.75900268555,-10.3800048828 -9.75900268555,-19.1199951172 -22.5800018311,-20.1600036621 c -0.919998168945,-0.0800018310547 -1.43899536133,1.04000854492 -0.800003051758,1.70001220703 c 0.0,0.0 5.12100219727,5.11999511719 5.12100219727,5.11999511719 c 0.378997802734,0.380004882812 0.97900390625,0.380004882812 1.37899780273,0.020004272461 c 0.0,0.0 2.10000610352,-1.94000244141 2.10000610352,-1.94000244141 Z M 2.73950195312,6.01550292969 c -6.26000976562,-2.83999633789 -10.7200012207,-8.76000976562 -11.8399963379,-15.8600006104 c -0.118011474609,-0.667007446289 -0.702011108398,-1.15100097656 -1.38000488281,-1.13999938965 c -0.860000610352,0.0 -1.52000427246,0.759994506836 -1.38000488281,1.61999511719 c 1.54000854492,10.4000091553 9.5,19.2200012207 22.4199981689,20.2799987793 c 0.920013427734,0.0800018310547 1.44100952148,-1.03999328613 0.800003051758,-1.69999694824 c 0.0,0.0 -5.11999511719,-5.11999511719 -5.11999511719,-5.11999511719 c -0.380004882812,-0.376007080078 -0.988998413086,-0.385009765625 -1.38000488281,-0.0200042724609 c 0.0,0.0 -2.11999511719,1.94000244141 -2.11999511719,1.94000244141 Z"
- android:fillColor="#FFFFFFFF"
- android:fillAlpha="0" />
- </group>
- </group>
- </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate_animation.xml b/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate_animation.xml
deleted file mode 100644
index 6d3fd37..0000000
--- a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate_animation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 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.
--->
-<animated-vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_portrait_from_auto_rotate" >
- <target
- android:name="device_0"
- android:animation="@anim/ic_rotate_to_portrait_device_0_animation" />
- <target
- android:name="device_merged"
- android:animation="@anim/ic_rotate_to_portrait_device_merged_animation" />
- <target
- android:name="arrows"
- android:animation="@anim/ic_rotate_to_portrait_arrows_animation" />
- <target
- android:name="arrows_0"
- android:animation="@anim/ic_rotate_to_portrait_arrows_0_animation" />
- <target
- android:name="bottom_merged"
- android:animation="@anim/ic_rotate_to_portrait_bottom_merged_animation" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_qs_auto_rotate.xml
similarity index 95%
rename from packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml
rename to packages/SystemUI/res/drawable/ic_qs_auto_rotate.xml
index bce494c..fba45d1 100644
--- a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_auto_rotate.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2017 The Android Open Source Project
+ Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -16,12 +16,10 @@
-->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
- android:name="ic_portrait_to_rotate"
android:height="48dp"
android:width="48dp"
android:viewportHeight="48"
- android:viewportWidth="48"
- android:tint="?android:attr/colorControlNormal" >
+ android:viewportWidth="48">
<group
android:name="device"
android:translateX="24"
@@ -61,4 +59,3 @@
</group>
</group>
</vector>
-
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
index 5e7cd97..56223db 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
@@ -18,7 +18,7 @@
android:height="32dp"
android:viewportWidth="6.0"
android:viewportHeight="24.0"
- android:tint="?android:attr/textColorSecondary">
+ android:tint="?android:attr/colorAccent">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6.000000,15.700000l-3.000000,5.599999 -3.000000,-5.599999z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
index 0dca1db..12c1a7a 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
@@ -18,7 +18,7 @@
android:height="32dp"
android:viewportWidth="6.0"
android:viewportHeight="24.0"
- android:tint="?android:attr/textColorSecondary">
+ android:tint="?android:attr/colorAccent">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M0.000000,13.700000l3.000000,-5.700000 3.000000,5.700000z"/>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7a670fd..0134086 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -618,6 +618,8 @@
type icon is wide for the tile in quick settings. -->
<dimen name="wide_type_icon_start_padding_qs">3dp</dimen>
+ <dimen name="signal_indicator_to_icon_frame_spacing">3dp</dimen>
+
<!-- The maximum width of the navigation bar ripples. -->
<dimen name="key_button_ripple_max_width">95dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 90c5977..4837fef 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -476,7 +476,6 @@
<style name="TextAppearance.NotificationInfo.Button">
<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:textSize">14sp</item>
- <item name="android:textAllCaps">true</item>
<item name="android:textColor">?android:attr/colorAccent</item>
<item name="android:background">@drawable/btn_borderless_rect</item>
<item name="android:gravity">center</item>
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index a7d1f0d..0be522b 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -51,7 +51,8 @@
*/
public class OverviewProxyService implements CallbackController<OverviewProxyListener>, Dumpable {
- private static final String TAG = "OverviewProxyService";
+ public static final String TAG_OPS = "OverviewProxyService";
+ public static final boolean DEBUG_OVERVIEW_PROXY = false;
private static final long BACKOFF_MILLIS = 5000;
private final Context mContext;
@@ -96,12 +97,12 @@
try {
service.linkToDeath(mOverviewServiceDeathRcpt, 0);
} catch (RemoteException e) {
- Log.e(TAG, "Lost connection to launcher service", e);
+ Log.e(TAG_OPS, "Lost connection to launcher service", e);
}
try {
mOverviewProxy.onBind(mSysUiProxy);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to call onBind()", e);
+ Log.e(TAG_OPS, "Failed to call onBind()", e);
}
notifyConnectionChanged();
}
@@ -211,7 +212,7 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(TAG + " state:");
+ pw.println(TAG_OPS + " state:");
pw.print(" mConnectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
pw.print(" isCurrentUserSetup="); pw.println(mDeviceProvisionedController
.isCurrentUserSetup());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
index 9ee40cc..d9583af 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
@@ -41,6 +41,7 @@
private ImageView mOut;
private int mWideOverlayIconStartPadding;
+ private int mSignalIndicatorToIconFrameSpacing;
public SignalTileView(Context context) {
super(context);
@@ -48,8 +49,13 @@
mIn = addTrafficView(R.drawable.ic_qs_signal_in);
mOut = addTrafficView(R.drawable.ic_qs_signal_out);
+ setClipChildren(false);
+ setClipToPadding(false);
+
mWideOverlayIconStartPadding = context.getResources().getDimensionPixelSize(
R.dimen.wide_type_icon_start_padding_qs);
+ mSignalIndicatorToIconFrameSpacing = context.getResources().getDimensionPixelSize(
+ R.dimen.signal_indicator_to_icon_frame_spacing);
}
private ImageView addTrafficView(int icon) {
@@ -99,10 +105,10 @@
boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
int left, right;
if (isRtl) {
- right = mIconFrame.getLeft();
+ right = getLeft() - mSignalIndicatorToIconFrameSpacing;
left = right - indicator.getMeasuredWidth();
} else {
- left = mIconFrame.getRight();
+ left = getRight() + mSignalIndicatorToIconFrameSpacing;
right = left + indicator.getMeasuredWidth();
}
indicator.layout(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index acd327b..b4cfda6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -85,6 +85,8 @@
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(0, padding, 0, padding);
mIconFrame.addView(mIcon, params);
+ mIconFrame.setClipChildren(false);
+ mIconFrame.setClipToPadding(false);
mTileBackground = newTileBackground();
if (mTileBackground instanceof RippleDrawable) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 1e00894..60422ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -36,20 +36,8 @@
/** Quick settings tile: Rotation **/
public class RotationLockTile extends QSTileImpl<BooleanState> {
- private final AnimationIcon mPortraitToAuto
- = new AnimationIcon(R.drawable.ic_portrait_to_auto_rotate_animation,
- R.drawable.ic_portrait_from_auto_rotate);
- private final AnimationIcon mAutoToPortrait
- = new AnimationIcon(R.drawable.ic_portrait_from_auto_rotate_animation,
- R.drawable.ic_portrait_to_auto_rotate);
- private final AnimationIcon mLandscapeToAuto
- = new AnimationIcon(R.drawable.ic_landscape_to_auto_rotate_animation,
- R.drawable.ic_landscape_from_auto_rotate);
- private final AnimationIcon mAutoToLandscape
- = new AnimationIcon(R.drawable.ic_landscape_from_auto_rotate_animation,
- R.drawable.ic_landscape_to_auto_rotate);
-
+ private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_auto_rotate);
private final RotationLockController mController;
public RotationLockTile(QSHost host) {
@@ -93,19 +81,10 @@
protected void handleUpdateState(BooleanState state, Object arg) {
if (mController == null) return;
final boolean rotationLocked = mController.isRotationLocked();
- // TODO: Handle accessibility rotation lock and whatnot.
state.value = !rotationLocked;
- final boolean portrait = isCurrentOrientationLockPortrait(mController, mContext);
- if (rotationLocked) {
- final int label = portrait ? R.string.quick_settings_rotation_locked_portrait_label
- : R.string.quick_settings_rotation_locked_landscape_label;
- state.label = mContext.getString(label);
- state.icon = portrait ? mAutoToPortrait : mAutoToLandscape;
- } else {
- state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label);
- state.icon = portrait ? mPortraitToAuto : mLandscapeToAuto;
- }
+ state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label);
+ state.icon = mIcon;
state.contentDescription = getAccessibilityString(rotationLocked);
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
@@ -134,18 +113,7 @@
* @param locked Whether or not rotation is locked.
*/
private String getAccessibilityString(boolean locked) {
- if (locked) {
- return mContext.getString(R.string.accessibility_quick_settings_rotation_value,
- isCurrentOrientationLockPortrait(mController, mContext)
- ? mContext.getString(
- R.string.quick_settings_rotation_locked_portrait_label)
- : mContext.getString(
- R.string.quick_settings_rotation_locked_landscape_label))
- + "," + mContext.getString(R.string.accessibility_quick_settings_rotation);
-
- } else {
- return mContext.getString(R.string.accessibility_quick_settings_rotation);
- }
+ return mContext.getString(R.string.accessibility_quick_settings_rotation);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 4faa84a..ff923e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -43,6 +43,8 @@
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
+import static com.android.systemui.OverviewProxyService.TAG_OPS;
/**
* Class to detect gestures on the navigation bar.
@@ -118,6 +120,9 @@
event.transform(mTransformGlobalMatrix);
try {
overviewProxy.onMotionEvent(event);
+ if (DEBUG_OVERVIEW_PROXY) {
+ Log.d(TAG_OPS, "Send MotionEvent: " + event.toString());
+ }
return true;
} catch (RemoteException e) {
Log.e(TAG, "Callback failed", e);
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 f41cb29..6857337 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -39,7 +39,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
import android.graphics.drawable.Icon;
import android.media.AudioManager;
import android.net.Uri;
@@ -66,7 +65,6 @@
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
@@ -146,7 +144,6 @@
private boolean mDockedStackExists;
private boolean mManagedProfileIconVisible = false;
- private boolean mManagedProfileInQuietMode = false;
private BluetoothController mBluetooth;
@@ -474,17 +471,6 @@
}
}
- private void updateQuietState() {
- mManagedProfileInQuietMode = false;
- int currentUserId = ActivityManager.getCurrentUser();
- for (UserInfo ui : mUserManager.getEnabledProfiles(currentUserId)) {
- if (ui.isManagedProfile() && ui.isQuietModeEnabled()) {
- mManagedProfileInQuietMode = true;
- return;
- }
- }
- }
-
private void updateManagedProfile() {
// getLastResumedActivityUserId needds to acquire the AM lock, which may be contended in
// some cases. Since it doesn't really matter here whether it's updated in this frame
@@ -502,11 +488,6 @@
mIconController.setIcon(mSlotManagedProfile,
R.drawable.stat_sys_managed_profile_status,
mContext.getString(R.string.accessibility_managed_profile));
- } else if (mManagedProfileInQuietMode) {
- showIcon = true;
- mIconController.setIcon(mSlotManagedProfile,
- R.drawable.stat_sys_managed_profile_status_off,
- mContext.getString(R.string.accessibility_managed_profile));
} else {
showIcon = false;
}
@@ -676,7 +657,6 @@
public void onUserSwitchComplete(int newUserId) throws RemoteException {
mHandler.post(() -> {
updateAlarm();
- updateQuietState();
updateManagedProfile();
updateForegroundInstantApps();
});
@@ -724,7 +704,6 @@
if (mCurrentUserSetup == userSetup) return;
mCurrentUserSetup = userSetup;
updateAlarm();
- updateQuietState();
}
@Override
@@ -793,7 +772,6 @@
} else if (action.equals(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) ||
action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) ||
action.equals(Intent.ACTION_MANAGED_PROFILE_REMOVED)) {
- updateQuietState();
updateManagedProfile();
} else if (action.equals(AudioManager.ACTION_HEADSET_PLUG)) {
updateHeadsetPlug(intent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
index afd2236..6fc5bbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -49,6 +49,8 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
+import static com.android.systemui.OverviewProxyService.TAG_OPS;
/**
* Class to detect gestures on the navigation bar and implement quick scrub and switch.
@@ -144,6 +146,9 @@
try {
final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
overviewProxy.onQuickSwitch();
+ if (DEBUG_OVERVIEW_PROXY) {
+ Log.d(TAG_OPS, "Quick Switch");
+ }
} catch (RemoteException e) {
Log.e(TAG, "Failed to send start of quick switch.", e);
}
@@ -256,6 +261,9 @@
if (mQuickScrubActive) {
try {
overviewProxy.onQuickScrubProgress(scrubFraction);
+ if (DEBUG_OVERVIEW_PROXY) {
+ Log.d(TAG_OPS, "Quick Scrub Progress:" + scrubFraction);
+ }
} catch (RemoteException e) {
Log.e(TAG, "Failed to send progress of quick scrub.", e);
}
@@ -355,6 +363,9 @@
mTrackAnimator.start();
try {
mOverviewEventSender.getProxy().onQuickScrubStart();
+ if (DEBUG_OVERVIEW_PROXY) {
+ Log.d(TAG_OPS, "Quick Scrub Start");
+ }
} catch (RemoteException e) {
Log.e(TAG, "Failed to send start of quick scrub.", e);
}
@@ -369,6 +380,9 @@
mQuickScrubEndAnimator.start();
try {
mOverviewEventSender.getProxy().onQuickScrubEnd();
+ if (DEBUG_OVERVIEW_PROXY) {
+ Log.d(TAG_OPS, "Quick Scrub End");
+ }
} catch (RemoteException e) {
Log.e(TAG, "Failed to send end of quick scrub.", e);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 48c678e..29d33ce 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -294,6 +294,7 @@
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.display.DisplayManagerInternal;
import android.location.LocationManager;
import android.media.audiofx.AudioEffect;
import android.metrics.LogMaker;
@@ -474,6 +475,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@@ -25142,6 +25144,11 @@
public void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage) {
mUserController.setSwitchingToSystemUserMessage(switchingToSystemUserMessage);
}
+
+ @Override
+ public int getMaxRunningUsers() {
+ return mUserController.mMaxRunningUsers;
+ }
}
/**
@@ -25352,9 +25359,18 @@
}
}
}
- if (updateFrameworkRes && mWindowManager != null) {
- ActivityThread.currentActivityThread().getExecutor().execute(
- mWindowManager::onOverlayChanged);
+ if (updateFrameworkRes) {
+ // Update system server components that need to know about changed overlays. Because the
+ // overlay is applied in ActivityThread, we need to serialize through its thread too.
+ final Executor executor = ActivityThread.currentActivityThread().getExecutor();
+ final DisplayManagerInternal display =
+ LocalServices.getService(DisplayManagerInternal.class);
+ if (display != null) {
+ executor.execute(display::onOverlayChanged);
+ }
+ if (mWindowManager != null) {
+ executor.execute(mWindowManager::onOverlayChanged);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 35465a7..8910274 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -738,9 +738,18 @@
}
return;
}
+ final boolean showFirstCrash = Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.SHOW_FIRST_CRASH_DIALOG, 0) != 0;
+ final boolean showFirstCrashDevOption = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
+ 0,
+ UserHandle.USER_CURRENT) != 0;
final boolean crashSilenced = mAppsNotReportingCrashes != null &&
mAppsNotReportingCrashes.contains(proc.info.packageName);
- if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced) {
+ if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced
+ && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
proc.crashDialog = new AppErrorDialog(mContext, mService, data);
} else {
// The device is asleep, so just pretend that the user
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c7210a8..65bebc6 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -796,7 +796,7 @@
private void stopGuestOrEphemeralUserIfBackground(int oldUserId) {
if (DEBUG_MU) Slog.i(TAG, "Stop guest or ephemeral user if background: " + oldUserId);
UserState oldUss = mStartedUsers.get(oldUserId);
- if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId
+ if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId || oldUss == null
|| oldUss.state == UserState.STATE_STOPPING
|| oldUss.state == UserState.STATE_SHUTDOWN) {
return;
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 839ab4d..3a8e291 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -143,6 +143,9 @@
public void requestDisplayModesInTransactionLocked(int colorMode, int modeId) {
}
+ public void onOverlayChangedLocked() {
+ }
+
/**
* Sets the display layer stack while in a transaction.
*/
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9f18045..b97de65 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2019,8 +2019,8 @@
@Override
public void onOverlayChanged() {
synchronized (mSyncRoot) {
- if (updateLogicalDisplaysLocked()) {
- scheduleTraversalLocked(false);
+ for (int i = 0; i < mDisplayDevices.size(); i++) {
+ mDisplayDevices.get(i).onOverlayChangedLocked();
}
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 23e4c9b..fc3c1f7 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -600,6 +600,11 @@
}
}
+ @Override
+ public void onOverlayChangedLocked() {
+ updateDeviceInfoLocked();
+ }
+
public boolean requestModeInTransactionLocked(int modeId) {
if (modeId == 0) {
modeId = mDefaultModeId;
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index e158819..6dc5403 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -2628,6 +2628,10 @@
s.append(" mStarted=").append(mStarted).append('\n');
s.append(" mFixInterval=").append(mFixInterval).append('\n');
s.append(" mLowPowerMode=").append(mLowPowerMode).append('\n');
+ s.append(" mGnssMeasurementsProvider.isRegistered()=")
+ .append(mGnssMeasurementsProvider.isRegistered()).append('\n');
+ s.append(" mGnssNavigationMessageProvider.isRegistered()=")
+ .append(mGnssNavigationMessageProvider.isRegistered()).append('\n');
s.append(" mDisableGps (battery saver mode)=").append(mDisableGps).append('\n');
s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities));
s.append(" ( ");
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index 58a9516..fcdb9d1 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -46,7 +46,8 @@
private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>();
- private boolean mIsRegistered; // must access only on handler thread
+ private volatile boolean mIsRegistered; // must access only on handler thread, or read-only
+
private boolean mHasIsSupported;
private boolean mIsSupported;
@@ -58,6 +59,11 @@
mTag = name;
}
+ // read-only access for a dump() thread assured via volatile
+ public boolean isRegistered() {
+ return mIsRegistered;
+ }
+
public boolean addListener(@NonNull TListener listener) {
Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
IBinder binder = listener.asBinder();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 7bd1ae9..07ea51b 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2028,6 +2028,10 @@
vaultParams, vaultChallenge, secrets);
}
+ public void closeSession(@NonNull String sessionId) throws RemoteException {
+ mRecoverableKeyStoreManager.closeSession(sessionId);
+ }
+
@Override
public Map<String, byte[]> recoverKeys(@NonNull String sessionId,
@NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys)
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 801dc54..76508d5 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -16,12 +16,12 @@
package com.android.server.locksettings.recoverablekeystore;
-import static android.security.keystore.RecoveryManager.ERROR_BAD_CERTIFICATE_FORMAT;
-import static android.security.keystore.RecoveryManager.ERROR_DECRYPTION_FAILED;
-import static android.security.keystore.RecoveryManager.ERROR_INSECURE_USER;
-import static android.security.keystore.RecoveryManager.ERROR_NO_SNAPSHOT_PENDING;
-import static android.security.keystore.RecoveryManager.ERROR_SERVICE_INTERNAL_ERROR;
-import static android.security.keystore.RecoveryManager.ERROR_SESSION_EXPIRED;
+import static android.security.keystore.RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT;
+import static android.security.keystore.RecoveryController.ERROR_DECRYPTION_FAILED;
+import static android.security.keystore.RecoveryController.ERROR_INSECURE_USER;
+import static android.security.keystore.RecoveryController.ERROR_NO_SNAPSHOT_PENDING;
+import static android.security.keystore.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR;
+import static android.security.keystore.RecoveryController.ERROR_SESSION_EXPIRED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -35,8 +35,8 @@
import android.security.keystore.KeychainProtectionParams;
import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.RecoveryController;
import android.security.keystore.WrappedApplicationKey;
-import android.security.keystore.RecoveryManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -63,7 +63,7 @@
import javax.crypto.AEADBadTagException;
/**
- * Class with {@link RecoveryManager} API implementation and internal methods to interact
+ * Class with {@link RecoveryController} API implementation and internal methods to interact
* with {@code LockSettingsService}.
*
* @hide
@@ -435,6 +435,13 @@
}
}
+ /**
+ * Destroys the session with the given {@code sessionId}.
+ */
+ public void closeSession(@NonNull String sessionId) throws RemoteException {
+ mRecoverySessionStorage.remove(Binder.getCallingUid(), sessionId);
+ }
+
public void removeKey(@NonNull String alias) throws RemoteException {
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
index 0042e10..c33c9de 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
@@ -16,8 +16,8 @@
package com.android.server.locksettings.recoverablekeystore;
+import android.security.keystore.RecoveryController;
import android.util.Log;
-import android.security.keystore.RecoveryManager;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
@@ -97,7 +97,7 @@
/*nonce=*/ cipher.getIV(),
/*keyMaterial=*/ encryptedKeyMaterial,
/*platformKeyGenerationId=*/ wrappingKey.getGenerationId(),
- RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+ RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
}
/**
@@ -107,14 +107,14 @@
* @param keyMaterial The encrypted bytes of the key material.
* @param platformKeyGenerationId The generation ID of the key used to wrap this key.
*
- * @see RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS
+ * @see RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS
* @hide
*/
public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId) {
mNonce = nonce;
mKeyMaterial = keyMaterial;
mPlatformKeyGenerationId = platformKeyGenerationId;
- mRecoveryStatus = RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS;
+ mRecoveryStatus = RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS;
}
/**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java
index f7633e4..0e66746 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java
@@ -73,6 +73,16 @@
}
/**
+ * Deletes the session with {@code sessionId} created by app with {@code uid}.
+ */
+ public void remove(int uid, String sessionId) {
+ if (mSessionsByUid.get(uid) == null) {
+ return;
+ }
+ mSessionsByUid.get(uid).removeIf(session -> session.mSessionId.equals(sessionId));
+ }
+
+ /**
* Removes all sessions associated with the given recovery agent uid.
*
* @param uid The uid of the recovery agent whose sessions to remove.
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 4c00921..2584187 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -160,7 +160,7 @@
}
private boolean isCall(NotificationRecord record) {
- return record.getNotification().category == Notification.CATEGORY_CALL
+ return record.isCategory(Notification.CATEGORY_CALL)
&& isDefaultPhoneApp(record.sbn.getPackageName());
}
diff --git a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java b/services/core/java/com/android/server/pm/CrossProfileAppsService.java
similarity index 95%
rename from services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java
rename to services/core/java/com/android/server/pm/CrossProfileAppsService.java
index 0913269..027a302 100644
--- a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsService.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.pm.crossprofile;
+package com.android.server.pm;
import android.content.Context;
diff --git a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
similarity index 98%
rename from services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
rename to services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index a517d6d..2007a0e 100644
--- a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.pm.crossprofile;
+package com.android.server.pm;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
@@ -25,14 +25,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ICrossProfileApps;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
-import android.content.pm.crossprofile.ICrossProfileApps;
-import android.graphics.Rect;
import android.os.Binder;
-import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a43818a..cc448da 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -17,10 +17,12 @@
package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_DEX_METADATA;
import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_WRONLY;
@@ -96,6 +98,7 @@
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
+import android.content.pm.dex.DexMetadataHelper;
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -259,6 +262,7 @@
// entries like "lost+found".
if (file.isDirectory()) return false;
if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
+ if (DexMetadataHelper.isDexMetadataFile(file)) return false;
return true;
}
};
@@ -939,6 +943,15 @@
mInstallerPackageName, mInstallerUid, user, mSigningDetails);
}
+ private static void maybeRenameFile(File from, File to) throws PackageManagerException {
+ if (!from.equals(to)) {
+ if (!from.renameTo(to)) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Could not rename file " + from + " to " + to);
+ }
+ }
+ }
+
/**
* Validate install by confirming that all application packages are have
* consistent package name, version code, and signing certificates.
@@ -983,6 +996,7 @@
if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
}
+
// Verify that all staged packages are internally consistent
final ArraySet<String> stagedSplits = new ArraySet<>();
for (File addedFile : addedFiles) {
@@ -1013,9 +1027,9 @@
// Take this opportunity to enforce uniform naming
final String targetName;
if (apk.splitName == null) {
- targetName = "base.apk";
+ targetName = "base" + APK_FILE_EXTENSION;
} else {
- targetName = "split_" + apk.splitName + ".apk";
+ targetName = "split_" + apk.splitName + APK_FILE_EXTENSION;
}
if (!FileUtils.isValidExtFilename(targetName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
@@ -1023,9 +1037,7 @@
}
final File targetFile = new File(mResolvedStageDir, targetName);
- if (!addedFile.equals(targetFile)) {
- addedFile.renameTo(targetFile);
- }
+ maybeRenameFile(addedFile, targetFile);
// Base is coming from session
if (apk.splitName == null) {
@@ -1033,6 +1045,18 @@
}
mResolvedStagedFiles.add(targetFile);
+
+ final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile);
+ if (dexMetadataFile != null) {
+ if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Invalid filename: " + dexMetadataFile);
+ }
+ final File targetDexMetadataFile = new File(mResolvedStageDir,
+ DexMetadataHelper.buildDexMetadataPathForApk(targetName));
+ mResolvedStagedFiles.add(targetDexMetadataFile);
+ maybeRenameFile(dexMetadataFile, targetDexMetadataFile);
+ }
}
if (removeSplitList.size() > 0) {
@@ -1097,6 +1121,12 @@
if (mResolvedBaseFile == null) {
mResolvedBaseFile = new File(appInfo.getBaseCodePath());
mResolvedInheritedFiles.add(mResolvedBaseFile);
+ // Inherit the dex metadata if present.
+ final File baseDexMetadataFile =
+ DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile);
+ if (baseDexMetadataFile != null) {
+ mResolvedInheritedFiles.add(baseDexMetadataFile);
+ }
}
// Inherit splits if not overridden
@@ -1107,6 +1137,12 @@
final boolean splitRemoved = removeSplitList.contains(splitName);
if (!stagedSplits.contains(splitName) && !splitRemoved) {
mResolvedInheritedFiles.add(splitFile);
+ // Inherit the dex metadata if present.
+ final File splitDexMetadataFile =
+ DexMetadataHelper.findDexMetadataForFile(splitFile);
+ if (splitDexMetadataFile != null) {
+ mResolvedInheritedFiles.add(splitDexMetadataFile);
+ }
}
}
}
@@ -1163,43 +1199,6 @@
}
/**
- * Calculate the final install footprint size, combining both staged and
- * existing APKs together and including unpacked native code from both.
- */
- private long calculateInstalledSize() throws PackageManagerException {
- Preconditions.checkNotNull(mResolvedBaseFile);
-
- final ApkLite baseApk;
- try {
- baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0);
- } catch (PackageParserException e) {
- throw PackageManagerException.from(e);
- }
-
- final List<String> splitPaths = new ArrayList<>();
- for (File file : mResolvedStagedFiles) {
- if (mResolvedBaseFile.equals(file)) continue;
- splitPaths.add(file.getAbsolutePath());
- }
- for (File file : mResolvedInheritedFiles) {
- if (mResolvedBaseFile.equals(file)) continue;
- splitPaths.add(file.getAbsolutePath());
- }
-
- // This is kind of hacky; we're creating a half-parsed package that is
- // straddled between the inherited and staged APKs.
- final PackageLite pkg = new PackageLite(null, baseApk, null, null, null, null,
- splitPaths.toArray(new String[splitPaths.size()]), null);
-
- try {
- return PackageHelper.calculateInstalledSize(pkg, params.abiOverride);
- } catch (IOException e) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Failed to calculate install size", e);
- }
- }
-
- /**
* Determine if creating hard links between source and destination is
* possible. That is, do they all live on the same underlying device.
*/
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 711ea13..dd374fe 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -191,6 +191,7 @@
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
import android.content.pm.VersionedPackage;
+import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.IArtManager;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -16486,6 +16487,7 @@
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
+ DexMetadataHelper.validatePackageDexMetadata(pkg);
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index bd1cd33..a33f071 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -18,6 +18,7 @@
import android.accounts.IAccountManager;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -46,6 +47,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
+import android.content.pm.dex.DexMetadataHelper;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.net.Uri;
@@ -72,13 +74,13 @@
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.SizedInputStream;
+import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import dalvik.system.DexFile;
import libcore.io.IoUtils;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -222,6 +224,8 @@
return runSetUserRestriction();
case "get-max-users":
return runGetMaxUsers();
+ case "get-max-running-users":
+ return runGetMaxRunningUsers();
case "set-home-activity":
return runSetHomeActivity();
case "set-installer":
@@ -1883,6 +1887,14 @@
return 0;
}
+ public int runGetMaxRunningUsers() {
+ ActivityManagerInternal activityManagerInternal =
+ LocalServices.getService(ActivityManagerInternal.class);
+ getOutPrintWriter().println("Maximum supported running users: "
+ + activityManagerInternal.getMaxRunningUsers());
+ return 0;
+ }
+
private static class InstallParams {
SessionParams sessionParams;
String installerPackageName;
@@ -2246,6 +2258,14 @@
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));
+ // Sanity check that all .dm files match an apk.
+ // (The installer does not support standalone .dm files and will not process them.)
+ try {
+ DexMetadataHelper.validateDexPaths(session.getNames());
+ } catch (IllegalStateException | IOException e) {
+ pw.println("Warning [Could not validate the dex paths: " + e.getMessage() + "]");
+ }
+
final LocalIntentReceiver receiver = new LocalIntentReceiver();
session.commit(receiver.getIntentSender());
@@ -2607,6 +2627,8 @@
pw.println("");
pw.println(" get-max-users");
pw.println("");
+ pw.println(" get-max-running-users");
+ pw.println("");
pw.println(" compile [-m MODE | -r REASON] [-f] [-c] [--split SPLIT_NAME]");
pw.println(" [--reset] [--check-prof (true | false)] (-a | TARGET-PACKAGE)");
pw.println(" Trigger compilation of TARGET-PACKAGE or all packages if \"-a\". Options are:");
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 261065f..b474c62 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -517,15 +517,25 @@
}
WindowState findMainWindow() {
+ return findMainWindow(true);
+ }
+
+ /**
+ * Finds the main window that either has type base application or application starting if
+ * requested.
+ *
+ * @param includeStartingApp Allow to search application-starting windows to also be returned.
+ * @return The main window of type base application or application starting if requested.
+ */
+ WindowState findMainWindow(boolean includeStartingApp) {
WindowState candidate = null;
- int j = mChildren.size();
- while (j > 0) {
- j--;
+ for (int j = mChildren.size() - 1; j >= 0; --j) {
final WindowState win = mChildren.get(j);
final int type = win.mAttrs.type;
// No need to loop through child window as base application and starting types can't be
// child windows.
- if (type == TYPE_BASE_APPLICATION || type == TYPE_APPLICATION_STARTING) {
+ if (type == TYPE_BASE_APPLICATION
+ || (includeStartingApp && type == TYPE_APPLICATION_STARTING)) {
// In cases where there are multiple windows, we prefer the non-exiting window. This
// happens for example when replacing windows during an activity relaunch. When
// constructing the animation, we want the new window, not the exiting one.
@@ -1373,8 +1383,11 @@
if (mLastTransactionSequence != mService.mTransactionSequence) {
mLastTransactionSequence = mService.mTransactionSequence;
- mNumInterestingWindows = mNumDrawnWindows = 0;
+ mNumDrawnWindows = 0;
startingDisplayed = false;
+
+ // There is the main base application window, even if it is exiting, wait for it
+ mNumInterestingWindows = findMainWindow(false /* includeStartingApp */) != null ? 1 : 0;
}
final WindowStateAnimator winAnimator = w.mWinAnimator;
@@ -1396,7 +1409,10 @@
if (w != startingWindow) {
if (w.isInteresting()) {
- mNumInterestingWindows++;
+ // Add non-main window as interesting since the main app has already been added
+ if (findMainWindow(false /* includeStartingApp */) != w) {
+ mNumInterestingWindows++;
+ }
if (w.isDrawnLw()) {
mNumDrawnWindows++;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 0cf8d2f..36e5d10 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -454,8 +454,11 @@
inputMethodManagerInternal.hideCurrentInputMethod();
mImeHideRequested = true;
}
+
+ // If a primary stack was just created, it will not have access to display content at
+ // this point so pass it from here to get a valid dock side.
final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
- mOriginalDockedSide = stack.getDockSide();
+ mOriginalDockedSide = stack.getDockSideForDisplay(mDisplayContent);
return;
}
mOriginalDockedSide = DOCKED_INVALID;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 8a36226..bc0f9ad 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1397,15 +1397,23 @@
return getDockSide(getRawBounds());
}
+ int getDockSideForDisplay(DisplayContent dc) {
+ return getDockSide(dc, getRawBounds());
+ }
+
private int getDockSide(Rect bounds) {
- if (!inSplitScreenWindowingMode()) {
- return DOCKED_INVALID;
- }
if (mDisplayContent == null) {
return DOCKED_INVALID;
}
- mDisplayContent.getBounds(mTmpRect);
- final int orientation = mDisplayContent.getConfiguration().orientation;
+ return getDockSide(mDisplayContent, bounds);
+ }
+
+ private int getDockSide(DisplayContent dc, Rect bounds) {
+ if (!inSplitScreenWindowingMode()) {
+ return DOCKED_INVALID;
+ }
+ dc.getBounds(mTmpRect);
+ final int orientation = dc.getConfiguration().orientation;
return getDockSideUnchecked(bounds, mTmpRect, orientation);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0fedc79..10e7893 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6613,7 +6613,7 @@
public void onOverlayChanged() {
synchronized (mWindowMap) {
mPolicy.onOverlayChangedLw();
- mDisplayManagerInternal.onOverlayChanged();
+ getDefaultDisplayContentLocked().updateDisplayInfo();
requestTraversal();
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3199bfa..e660c50 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -46,10 +46,10 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.IStorageManager;
-import android.util.TimingsTraceLog;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Slog;
+import android.util.TimingsTraceLog;
import android.view.WindowManager;
import com.android.internal.R;
@@ -57,20 +57,21 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BinderInternal;
-import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.util.ConcurrentUtils;
+import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.widget.ILockSettings;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.am.ActivityManagerService;
import com.android.server.audio.AudioService;
+import com.android.server.broadcastradio.BroadcastRadioService;
import com.android.server.camera.CameraServiceProxy;
import com.android.server.car.CarServiceHelperService;
import com.android.server.clipboard.ClipboardService;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.coverage.CoverageService;
import com.android.server.devicepolicy.DevicePolicyManagerService;
-import com.android.server.display.DisplayManagerService;
import com.android.server.display.ColorDisplayService;
+import com.android.server.display.DisplayManagerService;
import com.android.server.dreams.DreamManagerService;
import com.android.server.emergency.EmergencyAffordanceService;
import com.android.server.fingerprint.FingerprintService;
@@ -92,17 +93,16 @@
import com.android.server.os.DeviceIdentifiersPolicyService;
import com.android.server.os.SchedulingPolicyService;
import com.android.server.pm.BackgroundDexOptService;
+import com.android.server.pm.CrossProfileAppsService;
import com.android.server.pm.Installer;
import com.android.server.pm.LauncherAppsService;
import com.android.server.pm.OtaDexoptService;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.ShortcutService;
import com.android.server.pm.UserManagerService;
-import com.android.server.pm.crossprofile.CrossProfileAppsService;
import com.android.server.policy.PhoneWindowManager;
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
-import com.android.server.broadcastradio.BroadcastRadioService;
import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 767472a..402a37c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -283,6 +283,44 @@
}
@Test
+ public void closeSession_closesASession() throws Exception {
+ mRecoverableKeyStoreManager.startRecoverySession(
+ TEST_SESSION_ID,
+ TEST_PUBLIC_KEY,
+ TEST_VAULT_PARAMS,
+ TEST_VAULT_CHALLENGE,
+ ImmutableList.of(
+ new KeychainProtectionParams(
+ TYPE_LOCKSCREEN,
+ TYPE_PASSWORD,
+ KeyDerivationParams.createSha256Params(TEST_SALT),
+ TEST_SECRET)));
+
+ mRecoverableKeyStoreManager.closeSession(TEST_SESSION_ID);
+
+ assertEquals(0, mRecoverySessionStorage.size());
+ }
+
+ @Test
+ public void closeSession_doesNotCloseUnrelatedSessions() throws Exception {
+ mRecoverableKeyStoreManager.startRecoverySession(
+ TEST_SESSION_ID,
+ TEST_PUBLIC_KEY,
+ TEST_VAULT_PARAMS,
+ TEST_VAULT_CHALLENGE,
+ ImmutableList.of(
+ new KeychainProtectionParams(
+ TYPE_LOCKSCREEN,
+ TYPE_PASSWORD,
+ KeyDerivationParams.createSha256Params(TEST_SALT),
+ TEST_SECRET)));
+
+ mRecoverableKeyStoreManager.closeSession("some random session");
+
+ assertEquals(1, mRecoverySessionStorage.size());
+ }
+
+ @Test
public void startRecoverySession_throwsIfBadNumberOfSecrets() throws Exception {
try {
mRecoverableKeyStoreManager.startRecoverySession(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index 5cb7b67..f0254c6 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -28,8 +28,7 @@
import org.junit.runner.RunWith;
import android.content.Context;
-import android.content.SharedPreferences;
-import android.security.keystore.RecoveryManager;
+import android.security.keystore.RecoveryController;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -283,7 +282,7 @@
Map<String, Integer> statuses = mRecoverableKeyStoreDb.getStatusForAllKeys(uid);
assertThat(statuses).hasSize(3);
- assertThat(statuses).containsEntry(alias, RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+ assertThat(statuses).containsEntry(alias, RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
assertThat(statuses).containsEntry(alias2, status);
assertThat(statuses).containsEntry(alias3, status);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorageTest.java
index 6f93fe4..0f95748 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorageTest.java
@@ -19,6 +19,8 @@
import static junit.framework.Assert.fail;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -88,6 +90,45 @@
}
@Test
+ public void remove_deletesSpecificSession() {
+ RecoverySessionStorage storage = new RecoverySessionStorage();
+ storage.add(TEST_USER_ID, new RecoverySessionStorage.Entry(
+ TEST_SESSION_ID,
+ lskfHashFixture(),
+ keyClaimantFixture(),
+ vaultParamsFixture()));
+ storage.add(TEST_USER_ID, new RecoverySessionStorage.Entry(
+ "some other session",
+ lskfHashFixture(),
+ keyClaimantFixture(),
+ vaultParamsFixture()));
+
+ storage.remove(TEST_USER_ID, TEST_SESSION_ID);
+
+ assertNull(storage.get(TEST_USER_ID, TEST_SESSION_ID));
+ }
+
+ @Test
+ public void remove_doesNotDeleteOtherSessions() {
+ String otherSessionId = "some other session";
+ RecoverySessionStorage storage = new RecoverySessionStorage();
+ storage.add(TEST_USER_ID, new RecoverySessionStorage.Entry(
+ TEST_SESSION_ID,
+ lskfHashFixture(),
+ keyClaimantFixture(),
+ vaultParamsFixture()));
+ storage.add(TEST_USER_ID, new RecoverySessionStorage.Entry(
+ otherSessionId,
+ lskfHashFixture(),
+ keyClaimantFixture(),
+ vaultParamsFixture()));
+
+ storage.remove(TEST_USER_ID, TEST_SESSION_ID);
+
+ assertNotNull(storage.get(TEST_USER_ID, TEST_SESSION_ID));
+ }
+
+ @Test
public void destroy_overwritesLskfHashMemory() {
RecoverySessionStorage storage = new RecoverySessionStorage();
RecoverySessionStorage.Entry entry = new RecoverySessionStorage.Entry(
diff --git a/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java
rename to services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index ff55a2b..c69437dc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -1,9 +1,7 @@
-package com.android.server.pm.crossprofile;
+package com.android.server.pm;
import static com.google.common.truth.Truth.assertThat;
-import static junit.framework.Assert.assertEquals;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -34,7 +32,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@@ -44,7 +41,7 @@
/**
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.pm.crossprofile.CrossProfileAppsServiceImplTest
+ * atest FrameworksServicesTests:com.android.server.pm.CrossProfileAppsServiceImplTest
*/
@Presubmit
@RunWith(MockitoJUnitRunner.class)
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index 3dcd5b9..30fae01 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -78,6 +78,8 @@
private NotificationRecord mRecordCheater;
private NotificationRecord mRecordCheaterColorized;
private NotificationRecord mNoMediaSessionMedia;
+ private NotificationRecord mRecordColorized;
+ private NotificationRecord mRecordColorizedCall;
@Before
public void setUp() {
@@ -113,7 +115,6 @@
Notification n2 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setCategory(Notification.CATEGORY_CALL)
.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
- .setColorized(true /* colorized */)
.build();
mRecordHighCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
callPkg, 1, "highcall", callUid, callUid, n2,
@@ -200,13 +201,34 @@
pkg2, pkg2, 1, "cheater", uid2, uid2, n12, new UserHandle(userId),
"", 9258), getDefaultChannel());
mNoMediaSessionMedia.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
+
+ Notification n13 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .setColorized(true /* colorized */)
+ .build();
+ mRecordColorized = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
+ pkg2, 1, "colorized", uid2, uid2, n13,
+ new UserHandle(userId), "", 1999), getDefaultChannel());
+ mRecordHighCall.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
+
+ Notification n14 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setCategory(Notification.CATEGORY_CALL)
+ .setColorized(true)
+ .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ mRecordColorizedCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
+ callPkg, 1, "colorizedCall", callUid, callUid, n14,
+ new UserHandle(userId), "", 1999), getDefaultChannel());
+ mRecordColorizedCall.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
}
@Test
public void testOrdering() throws Exception {
final List<NotificationRecord> expected = new ArrayList<>();
- expected.add(mRecordHighCall);
+ expected.add(mRecordColorizedCall);
expected.add(mRecordDefaultMedia);
+ expected.add(mRecordColorized);
+ expected.add(mRecordHighCall);
expected.add(mRecordInlineReply);
expected.add(mRecordSms);
expected.add(mRecordStarredContact);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2fafdf5..423dc80 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -499,7 +499,7 @@
public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
private final Context mContext;
- private final INetworkPolicyManager mNetworkPolicy;
+ private INetworkPolicyManager mNetworkPolicy;
/**
* A listener class for monitoring changes to {@link SubscriptionInfo} records.
@@ -572,11 +572,9 @@
}
/** @hide */
- public SubscriptionManager(Context context) throws ServiceNotFoundException {
+ public SubscriptionManager(Context context) {
if (DBG) logd("SubscriptionManager created");
mContext = context;
- mNetworkPolicy = INetworkPolicyManager.Stub
- .asInterface(ServiceManager.getServiceOrThrow(Context.NETWORK_POLICY_SERVICE));
}
/**
@@ -589,6 +587,14 @@
.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
}
+ private final INetworkPolicyManager getNetworkPolicy() {
+ if (mNetworkPolicy == null) {
+ mNetworkPolicy = INetworkPolicyManager.Stub
+ .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+ }
+ return mNetworkPolicy;
+ }
+
/**
* Register for changes to the list of active {@link SubscriptionInfo} records or to the
* individual records themselves. When a change occurs the onSubscriptionsChanged method of
@@ -1686,7 +1692,7 @@
public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
try {
SubscriptionPlan[] subscriptionPlans =
- mNetworkPolicy.getSubscriptionPlans(subId, mContext.getOpPackageName());
+ getNetworkPolicy().getSubscriptionPlans(subId, mContext.getOpPackageName());
return subscriptionPlans == null
? Collections.emptyList() : Arrays.asList(subscriptionPlans);
} catch (RemoteException e) {
@@ -1715,7 +1721,7 @@
@SystemApi
public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
try {
- mNetworkPolicy.setSubscriptionPlans(subId,
+ getNetworkPolicy().setSubscriptionPlans(subId,
plans.toArray(new SubscriptionPlan[plans.size()]), mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1725,7 +1731,7 @@
/** @hide */
private String getSubscriptionPlansOwner(int subId) {
try {
- return mNetworkPolicy.getSubscriptionPlansOwner(subId);
+ return getNetworkPolicy().getSubscriptionPlansOwner(subId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}