Merge "Always show wifi wake setting."
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index bab2a85..231aaf2 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -232,7 +232,7 @@
while (state.keepRunning()) {
state.pauseTiming();
final MeasuredText text = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT)
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
.build();
diff --git a/api/current.txt b/api/current.txt
index d415f52..10fdea4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13048,6 +13048,8 @@
method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config);
method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config);
method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, android.graphics.Bitmap.Config);
+ method public static android.graphics.Bitmap createBitmap(android.graphics.Picture);
+ method public static android.graphics.Bitmap createBitmap(android.graphics.Picture, int, int, android.graphics.Bitmap.Config);
method public static android.graphics.Bitmap createScaledBitmap(android.graphics.Bitmap, int, int, boolean);
method public int describeContents();
method public void eraseColor(int);
@@ -14105,6 +14107,7 @@
method public void endRecording();
method public int getHeight();
method public int getWidth();
+ method public boolean requiresHardwareAcceleration();
method public deprecated void writeToStream(java.io.OutputStream);
}
@@ -16486,8 +16489,37 @@
package android.hardware.fingerprint {
public class FingerprintDialog {
- method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback);
- method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback);
+ method public void authenticate(android.hardware.fingerprint.FingerprintDialog.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintDialog.AuthenticationCallback);
+ method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintDialog.AuthenticationCallback);
+ field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
+ field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
+ field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
+ field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
+ field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
+ field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
+ field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
+ field public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; // 0xc
+ field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
+ field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
+ field public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; // 0x9
+ field public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; // 0xb
+ field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
+ field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
+ field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
+ field public static final int FINGERPRINT_ERROR_USER_CANCELED = 10; // 0xa
+ field public static final int FINGERPRINT_ERROR_VENDOR = 8; // 0x8
+ }
+
+ public static abstract class FingerprintDialog.AuthenticationCallback {
+ ctor public FingerprintDialog.AuthenticationCallback();
+ method public void onAuthenticationError(int, java.lang.CharSequence);
+ method public void onAuthenticationFailed();
+ method public void onAuthenticationHelp(int, java.lang.CharSequence);
+ method public void onAuthenticationSucceeded(android.hardware.fingerprint.FingerprintDialog.AuthenticationResult);
+ }
+
+ public static class FingerprintDialog.AuthenticationResult {
+ method public android.hardware.fingerprint.FingerprintDialog.CryptoObject getCryptoObject();
}
public static class FingerprintDialog.Builder {
@@ -16499,10 +16531,19 @@
method public android.hardware.fingerprint.FingerprintDialog.Builder setTitle(java.lang.CharSequence);
}
- public class FingerprintManager {
- method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
- method public boolean hasEnrolledFingerprints();
- method public boolean isHardwareDetected();
+ public static final class FingerprintDialog.CryptoObject {
+ ctor public FingerprintDialog.CryptoObject(java.security.Signature);
+ ctor public FingerprintDialog.CryptoObject(javax.crypto.Cipher);
+ ctor public FingerprintDialog.CryptoObject(javax.crypto.Mac);
+ method public javax.crypto.Cipher getCipher();
+ method public javax.crypto.Mac getMac();
+ method public java.security.Signature getSignature();
+ }
+
+ public deprecated class FingerprintManager {
+ method public deprecated void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
+ method public deprecated boolean hasEnrolledFingerprints();
+ method public deprecated boolean isHardwareDetected();
field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
@@ -16510,9 +16551,11 @@
field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
+ field public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; // 0xc
field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
field public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; // 0x9
+ field public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; // 0xb
field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
@@ -21946,6 +21989,7 @@
field public static final int ENCODING_DTS = 7; // 0x7
field public static final int ENCODING_DTS_HD = 8; // 0x8
field public static final int ENCODING_E_AC3 = 6; // 0x6
+ field public static final int ENCODING_E_AC3_JOC = 18; // 0x12
field public static final int ENCODING_IEC61937 = 13; // 0xd
field public static final int ENCODING_INVALID = 0; // 0x0
field public static final int ENCODING_MP3 = 9; // 0x9
@@ -24514,7 +24558,7 @@
field public static final int DIRECTIONALITY_HYPER_CARDIOID = 4; // 0x4
field public static final int DIRECTIONALITY_OMNI = 1; // 0x1
field public static final int DIRECTIONALITY_SUPER_CARDIOID = 5; // 0x5
- field public static final int DIRECTIONALITY_UNKNOW = 0; // 0x0
+ field public static final int DIRECTIONALITY_UNKNOWN = 0; // 0x0
field public static final int LOCATION_MAINBODY = 1; // 0x1
field public static final int LOCATION_MAINBODY_MOVABLE = 2; // 0x2
field public static final int LOCATION_PERIPHERAL = 3; // 0x3
@@ -33071,7 +33115,7 @@
field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
- field public static final java.lang.String DISALLOW_CONFIG_LOCATION_MODE = "no_config_location_mode";
+ field public static final java.lang.String DISALLOW_CONFIG_LOCATION = "no_config_location";
field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
field public static final java.lang.String DISALLOW_CONFIG_SCREEN_TIMEOUT = "no_config_screen_timeout";
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
diff --git a/api/system-current.txt b/api/system-current.txt
index 62cc2a3..ad71e7c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -19,6 +19,7 @@
field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
field public static final deprecated java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
+ field public static final java.lang.String BIND_DATA_SERVICE = "android.permission.BIND_DATA_SERVICE";
field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
@@ -1140,6 +1141,15 @@
package android.hardware.display {
+ public final class AmbientBrightnessDayStats implements android.os.Parcelable {
+ method public int describeContents();
+ method public float[] getBucketBoundaries();
+ method public java.time.LocalDate getLocalDate();
+ method public float[] getStats();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR;
+ }
+
public final class BrightnessChangeEvent implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 9af80e3..92bf24d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -298,6 +298,15 @@
package android.hardware.display {
+ public final class AmbientBrightnessDayStats implements android.os.Parcelable {
+ method public int describeContents();
+ method public float[] getBucketBoundaries();
+ method public java.time.LocalDate getLocalDate();
+ method public float[] getStats();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR;
+ }
+
public final class BrightnessChangeEvent implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index f75678b..6e0bd3a 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -26,6 +26,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -34,6 +35,7 @@
import libcore.io.Streams;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -583,7 +585,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null)) {
- Streams.copy(new FileInputStream(fd.getFileDescriptor()), System.out);
+ FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out);
}
}
}
@@ -596,7 +598,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "w", null, null)) {
- Streams.copy(System.in, new FileOutputStream(fd.getFileDescriptor()));
+ FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor());
}
}
}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 76dbd6a..b0019ac 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -38,8 +38,11 @@
src/external/StatsPuller.cpp \
src/external/StatsCompanionServicePuller.cpp \
src/external/SubsystemSleepStatePuller.cpp \
+ src/external/ResourceHealthManagerPuller.cpp \
src/external/CpuTimePerUidPuller.cpp \
src/external/CpuTimePerUidFreqPuller.cpp \
+ src/external/KernelUidCpuActiveTimeReader.cpp \
+ src/external/KernelUidCpuClusterTimeReader.cpp \
src/external/StatsPullerManagerImpl.cpp \
src/logd/LogEvent.cpp \
src/logd/LogListener.cpp \
@@ -75,7 +78,8 @@
$(LOCAL_PATH)/../../core/java
statsd_common_static_libraries := \
- libplatformprotos
+ libhealthhalutils \
+ libplatformprotos \
statsd_common_shared_libraries := \
libbase \
@@ -93,6 +97,7 @@
libhidlbase \
libhidltransport \
libhwbinder \
+ android.hardware.health@2.0 \
android.hardware.power@1.0 \
android.hardware.power@1.1 \
libmemunreachable
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index f545bb0..4e41454 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -551,6 +551,12 @@
return NO_ERROR;
}
+status_t StatsService::cmd_clear_puller_cache(FILE* out) {
+ mStatsPullerManager.ClearPullerCache();
+ fprintf(out, "Puller cached data removed!\n");
+ return NO_ERROR;
+}
+
Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
const vector<String16>& app) {
VLOG("StatsService::informAllUidData was called");
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index be20893..fd3ed1d 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -197,6 +197,11 @@
*/
status_t cmd_dump_memory_info(FILE* out);
+ /*
+ * Clear all puller cached data
+ */
+ status_t cmd_clear_puller_cache(FILE* out);
+
/**
* Update a configuration.
*/
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 4e570a6..e64b631 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -95,11 +95,12 @@
AppStartMemoryStateCaptured app_start_memory_state_captured = 55;
ShutdownSequenceReported shutdown_sequence_reported = 56;
BootSequenceReported boot_sequence_reported = 57;
+ DaveyOccurred davey_occurred = 58;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
// Pulled events will start at field 10000.
- // Next: 10019
+ // Next: 10021
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -120,6 +121,8 @@
CpuActiveTime cpu_active_time = 10016;
CpuClusterTime cpu_cluster_time = 10017;
DiskSpace disk_space = 10018;
+ RemainingBatteryCapacity remaining_battery_capacity = 10019;
+ FullBatteryCapacity full_battery_capacity = 10020;
}
}
@@ -721,6 +724,17 @@
}
/**
+ * Logs the duration of a davey (jank of >=700ms) when it occurs
+ *
+ * Logged from:
+ * frameworks/base/libs/hwui/JankTracker.cpp
+ */
+message DaveyOccurred {
+ // Amount of time it took to render the frame. Should be >=700ms.
+ optional int64 jank_duration_ms = 1;
+}
+
+/**
* Logs phone signal strength changes.
*
* Logged from:
@@ -757,8 +771,8 @@
// The tag used with the is_default for resetting sets of settings. This is generally null.
optional string tag = 5;
- // 1 indicates that this setting with tag should be resettable.
- optional int32 is_default = 6;
+ // True if this setting with tag should be resettable.
+ optional bool is_default = 6;
// The user ID associated. Defined in android/os/UserHandle.java
optional int32 user = 7;
@@ -1105,9 +1119,12 @@
optional int32 isolated_uid = 2;
- // 1 denotes we're creating an isolated uid and 0 denotes removal. We expect an isolated uid to
- // be removed before if it's used for another parent uid.
- optional int32 is_create = 3;
+ // We expect an isolated uid to be removed before if it's used for another parent uid.
+ enum Event {
+ REMOVED = 0;
+ CREATED = 1;
+ }
+ optional Event event = 3;
}
/**
@@ -1410,3 +1427,19 @@
// available bytes in download cache or temp directories
optional uint64 temp_available_bytes = 3;
}
+
+/**
+ * Pulls battery coulomb counter, which is the remaining battery charge in uAh.
+ * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ */
+message RemainingBatteryCapacity {
+ optional int32 charge_uAh = 1;
+}
+
+/**
+ * Pulls battery capacity, which is the battery capacity when full in uAh.
+ * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ */
+message FullBatteryCapacity {
+ optional int32 capacity_uAh = 1;
+}
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
new file mode 100644
index 0000000..72fb5ff
--- /dev/null
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include <android/hardware/health/2.0/IHealth.h>
+#include <healthhalutils/HealthHalUtils.h>
+#include "external/ResourceHealthManagerPuller.h"
+#include "external/StatsPuller.h"
+
+#include "ResourceHealthManagerPuller.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using android::hardware::hidl_vec;
+using android::hardware::health::V2_0::get_health_service;
+using android::hardware::health::V2_0::HealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::Return;
+using android::hardware::Void;
+
+using std::make_shared;
+using std::shared_ptr;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+sp<android::hardware::health::V2_0::IHealth> gHealthHal = nullptr;
+
+bool getHealthHal() {
+ if (gHealthHal == nullptr) {
+ gHealthHal = get_health_service();
+
+ }
+ return gHealthHal != nullptr;
+}
+
+ResourceHealthManagerPuller::ResourceHealthManagerPuller(int tagId) : StatsPuller(tagId) {
+}
+
+// TODO: add other health atoms (eg. Temperature).
+bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+ if (!getHealthHal()) {
+ ALOGE("Health Hal not loaded");
+ return false;
+ }
+
+ uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+
+ data->clear();
+ bool result_success = true;
+
+ Return<void> ret = gHealthHal->getHealthInfo([&](Result r, HealthInfo v) {
+ if (r != Result::SUCCESS) {
+ result_success = false;
+ return;
+ }
+ if (mTagId == android::util::REMAINING_BATTERY_CAPACITY) {
+ auto ptr = make_shared<LogEvent>(android::util::REMAINING_BATTERY_CAPACITY, timestamp);
+ ptr->write(v.legacy.batteryChargeCounter);
+ ptr->init();
+ data->push_back(ptr);
+ } else if (mTagId == android::util::FULL_BATTERY_CAPACITY) {
+ auto ptr = make_shared<LogEvent>(android::util::FULL_BATTERY_CAPACITY, timestamp);
+ ptr->write(v.legacy.batteryFullCharge);
+ ptr->init();
+ data->push_back(ptr);
+ } else {
+ ALOGE("Unsupported tag in ResourceHealthManagerPuller: %d", mTagId);
+ }
+ });
+ if (!result_success || !ret.isOk()) {
+ ALOGE("getHealthHal() failed: health HAL service not available. Description: %s",
+ ret.description().c_str());
+ if (!ret.isOk() && ret.isDeadObject()) {
+ gHealthHal = nullptr;
+ }
+ return false;
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.h b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
new file mode 100644
index 0000000..9b238ea
--- /dev/null
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads Ihealth.hal
+ */
+class ResourceHealthManagerPuller : public StatsPuller {
+public:
+ ResourceHealthManagerPuller(int tagId);
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index cadc535..da14434 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -59,6 +59,11 @@
return ret;
}
+void StatsPuller::ClearCache() {
+ lock_guard<std::mutex> lock(mLock);
+ mCachedData.clear();
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 47cc9f0..bc7c45f 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -38,6 +38,8 @@
bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
+ void ClearCache();
+
protected:
// The atom tag id this puller pulls
const int mTagId;
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 00a1475..4826d96 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -50,10 +50,14 @@
return mPullerManager.Pull(tagId, data);
}
- virtual void SetTimeBaseSec(const long timeBaseSec) {
+ void SetTimeBaseSec(const long timeBaseSec) {
mPullerManager.SetTimeBaseSec(timeBaseSec);
}
+ void ClearPullerCache() {
+ mPullerManager.ClearPullerCache();
+ }
+
private:
StatsPullerManagerImpl
& mPullerManager = StatsPullerManagerImpl::GetInstance();
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 148c9ae..71b0abe 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -23,10 +23,11 @@
#include <climits>
#include "CpuTimePerUidFreqPuller.h"
#include "CpuTimePerUidPuller.h"
-#include "SubsystemSleepStatePuller.h"
+#include "ResourceHealthManagerPuller.h"
#include "StatsCompanionServicePuller.h"
#include "StatsPullerManagerImpl.h"
#include "StatsService.h"
+#include "SubsystemSleepStatePuller.h"
#include "logd/LogEvent.h"
#include "statslog.h"
@@ -83,7 +84,10 @@
make_shared<StatsCompanionServicePuller>(android::util::WIFI_ACTIVITY_ENERGY_INFO)});
mPullers.insert({android::util::MODEM_ACTIVITY_INFO,
make_shared<StatsCompanionServicePuller>(android::util::MODEM_ACTIVITY_INFO)});
-
+ mPullers.insert({android::util::REMAINING_BATTERY_CAPACITY,
+ make_shared<ResourceHealthManagerPuller>(android::util::REMAINING_BATTERY_CAPACITY)});
+ mPullers.insert({android::util::FULL_BATTERY_CAPACITY,
+ make_shared<ResourceHealthManagerPuller>(android::util::FULL_BATTERY_CAPACITY)});
mStatsCompanionService = StatsService::getStatsCompanionService();
}
@@ -195,6 +199,12 @@
}
}
+void StatsPullerManagerImpl::ClearPullerCache() {
+ for (auto puller : mPullers) {
+ puller.second->ClearCache();
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 7c59f66..fba3ade 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -49,6 +49,8 @@
void SetTimeBaseSec(long timeBaseSec) {mTimeBaseSec = timeBaseSec;};
+ void ClearPullerCache();
+
private:
StatsPullerManagerImpl();
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index d0737de..6362895 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -189,13 +189,7 @@
return;
}
- if (event.GetTagId() != android::util::APP_HOOK) {
- std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
- if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) {
- VLOG("log source %d not on the whitelist", event.GetUid());
- return;
- }
- } else { // Check that app hook fields are valid.
+ if (event.GetTagId() == android::util::APP_HOOK) { // Check that app hook fields are valid.
// TODO: Find a way to make these checks easier to maintain if the app hooks get changed.
// Label is 2nd from last field and must be from [0, 15].
@@ -211,6 +205,21 @@
VLOG("App hook does not have valid state %ld", apphookState);
return;
}
+ } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) {
+ // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp.
+ // Check that the davey duration is reasonable. Max length check is for privacy.
+ status_t err = NO_ERROR;
+ long duration = event.GetLong(event.size(), &err);
+ if (err != NO_ERROR || duration > 100000) {
+ VLOG("Davey duration is unreasonably long: %ld", duration);
+ return;
+ }
+ } else {
+ std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
+ if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) {
+ VLOG("log source %d not on the whitelist", event.GetUid());
+ return;
+ }
}
int tagId = event.GetTagId();
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index d39aa1d..57575ae 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -116,28 +116,32 @@
findViewById(R.id.plug).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, 1);
+ StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
+ StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_AC);
}
});
findViewById(R.id.unplug).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, 0);
+ StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
+ StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_NONE);
}
});
findViewById(R.id.screen_on).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, 2);
+ StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
+ StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_ON);
}
});
findViewById(R.id.screen_off).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, 1);
+ StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
+ StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF);
}
});
@@ -255,7 +259,9 @@
}
int[] uids = new int[] {mUids[id]};
String[] tags = new String[] {"acquire"};
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, 0, name, 1);
+ StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
+ StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
StringBuilder sb = new StringBuilder();
sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
.append(", ").append(name).append(", 1);");
@@ -269,7 +275,9 @@
}
int[] uids = new int[] {mUids[id]};
String[] tags = new String[] {"release"};
- StatsLog.write(10, uids, tags, 0, name, 0);
+ StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
+ StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
StringBuilder sb = new StringBuilder();
sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
.append(", ").append(name).append(", 0);");
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 05e51a4..58936fc 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -15,6 +15,7 @@
Landroid/app/ActivityManager;->mContext:Landroid/content/Context;
Landroid/app/ActivityManagerNative;->getDefault()Landroid/app/IActivityManager;
Landroid/app/ActivityManager;->PROCESS_STATE_TOP:I
+Landroid/app/ActivityManager$RecentTaskInfo;->firstActiveTime:J
Landroid/app/ActivityManager$RunningAppProcessInfo;->flags:I
Landroid/app/ActivityManager$RunningAppProcessInfo;->processState:I
Landroid/app/Activity;->mApplication:Landroid/app/Application;
@@ -24,6 +25,7 @@
Landroid/app/Activity;->mToken:Landroid/os/IBinder;
Landroid/app/Activity;->mWindow:Landroid/view/Window;
Landroid/app/Activity;->mWindowManager:Landroid/view/WindowManager;
+Landroid/app/Activity;->setDisablePreviewScreenshots(Z)V
Landroid/app/ActivityThread$ActivityClientRecord;->activityInfo:Landroid/content/pm/ActivityInfo;
Landroid/app/ActivityThread$ActivityClientRecord;->token:Landroid/os/IBinder;
Landroid/app/ActivityThread$AppBindData;->info:Landroid/app/LoadedApk;
@@ -31,6 +33,7 @@
Landroid/app/ActivityThread;->currentActivityThread()Landroid/app/ActivityThread;
Landroid/app/ActivityThread;->currentApplication()Landroid/app/Application;
Landroid/app/ActivityThread;->currentPackageName()Ljava/lang/String;
+Landroid/app/ActivityThread;->currentProcessName()Ljava/lang/String;
Landroid/app/ActivityThread;->getApplication()Landroid/app/Application;
Landroid/app/ActivityThread;->getApplicationThread()Landroid/app/ActivityThread$ApplicationThread;
Landroid/app/ActivityThread;->getHandler()Landroid/os/Handler;
@@ -71,6 +74,9 @@
Landroid/app/ApplicationLoaders;->mLoaders:Landroid/util/ArrayMap;
Landroid/app/Application;->mComponentCallbacks:Ljava/util/ArrayList;
Landroid/app/Application;->mLoadedApk:Landroid/app/LoadedApk;
+Landroid/app/ApplicationPackageManager;->deletePackage(Ljava/lang/String;Landroid/content/pm/IPackageDeleteObserver;I)V
+Landroid/app/ApplicationPackageManager;->installExistingPackage(Ljava/lang/String;)I
+Landroid/app/ApplicationPackageManager;->installExistingPackage(Ljava/lang/String;I)I
Landroid/app/AppOpsManager;->checkOp(IILjava/lang/String;)I
Landroid/app/AppOpsManager;->checkOpNoThrow(IILjava/lang/String;)I
Landroid/app/AppOpsManager;->noteOp(I)I
@@ -182,18 +188,24 @@
Landroid/content/Intent;->putExtra(Ljava/lang/String;Landroid/os/IBinder;)Landroid/content/Intent;
Landroid/content/pm/ApplicationInfo;->installLocation:I
Landroid/content/pm/ApplicationInfo;->isForwardLocked()Z
+Landroid/content/pm/ApplicationInfo;->isInstantApp()Z
Landroid/content/pm/ApplicationInfo;->isPrivilegedApp()Z
Landroid/content/pm/ApplicationInfo;->primaryCpuAbi:Ljava/lang/String;
Landroid/content/pm/ApplicationInfo;->privateFlags:I
+Landroid/content/pm/ApplicationInfo;->targetSandboxVersion:I
Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
Landroid/content/pm/IPackageManager;->setLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;ILandroid/content/IntentFilter;ILandroid/content/ComponentName;)V
Landroid/content/pm/LauncherApps;->mPm:Landroid/content/pm/PackageManager;
Landroid/content/pm/LauncherApps;->startShortcut(Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Rect;Landroid/os/Bundle;I)V
+Landroid/content/pm/PackageInstaller$SessionParams;->setGrantedRuntimePermissions([Ljava/lang/String;)V
+Landroid/content/pm/PackageInstaller$SessionParams;->setInstallAsInstantApp(Z)V
Landroid/content/pm/PackageManager;->freeStorageAndNotify(JLandroid/content/pm/IPackageDataObserver;)V
Landroid/content/pm/PackageManager;->freeStorageAndNotify(Ljava/lang/String;JLandroid/content/pm/IPackageDataObserver;)V
Landroid/content/pm/PackageManager;->freeStorage(JLandroid/content/IntentSender;)V
Landroid/content/pm/PackageManager;->freeStorage(Ljava/lang/String;JLandroid/content/IntentSender;)V
+Landroid/content/pm/PackageManager;->getApplicationInfoAsUser(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo;
Landroid/content/pm/PackageManager;->getPackageCandidateVolumes(Landroid/content/pm/ApplicationInfo;)Ljava/util/List;
+Landroid/content/pm/PackageManager;->getPackageInfoAsUser(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
Landroid/content/pm/PackageManager;->getPackageSizeInfo(Ljava/lang/String;Landroid/content/pm/IPackageStatsObserver;)V
Landroid/content/pm/PackageManager;->getResourcesForApplicationAsUser(Ljava/lang/String;I)Landroid/content/res/Resources;
Landroid/content/pm/PackageManager;->movePackage(Ljava/lang/String;Landroid/os/storage/VolumeInfo;)I
@@ -217,6 +229,7 @@
Landroid/content/res/AssetManager;->loadResourceBagValue(IILandroid/util/TypedValue;Z)I
Landroid/content/res/AssetManager;->loadResourceValue(ISLandroid/util/TypedValue;Z)I
Landroid/content/res/AssetManager;->loadThemeAttributeValue(JILandroid/util/TypedValue;Z)I
+Landroid/content/res/AssetManager;->mObject:J
Landroid/content/res/AssetManager;->openNonAssetFdNative(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;
Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;I)Ljava/io/InputStream;
Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;)Ljava/io/InputStream;
@@ -276,6 +289,7 @@
Landroid/graphics/drawable/BitmapDrawable;->getTintMode()Landroid/graphics/PorterDuff$Mode;
Landroid/graphics/drawable/BitmapDrawable;->setBitmap(Landroid/graphics/Bitmap;)V
Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mConstantPadding:Landroid/graphics/Rect;
+Landroid/graphics/drawable/DrawableContainer;->getOpticalInsets()Landroid/graphics/Insets;
Landroid/graphics/drawable/DrawableContainer;->mDrawableContainerState:Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;
Landroid/graphics/drawable/Drawable;->inflateWithAttributes(Landroid/content/res/Resources;Lorg/xmlpull/v1/XmlPullParser;Landroid/content/res/TypedArray;I)V
Landroid/graphics/drawable/Drawable;->mCallback:Ljava/lang/ref/WeakReference;
@@ -358,9 +372,25 @@
Landroid/net/ConnectivityManager;->mService:Landroid/net/IConnectivityManager;
Landroid/net/ConnectivityManager;->requestRouteToHostAddress(ILjava/net/InetAddress;)Z
Landroid/net/ConnectivityManager;->requestRouteToHost(II)Z
+Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String;
Landroid/net/LinkProperties;->setHttpProxy(Landroid/net/ProxyInfo;)V
Landroid/net/NetworkPolicyManager;->mService:Landroid/net/INetworkPolicyManager;
+Landroid/net/NetworkStats;->capacity:I
+Landroid/net/NetworkStats;->defaultNetwork:[I
+Landroid/net/NetworkStats;->iface:[Ljava/lang/String;
+Landroid/net/NetworkStats;->metered:[I
+Landroid/net/NetworkStats;->operations:[J
+Landroid/net/NetworkStats;->roaming:[I
+Landroid/net/NetworkStats;->rxBytes:[J
+Landroid/net/NetworkStats;->rxPackets:[J
+Landroid/net/NetworkStats;->set:[I
+Landroid/net/NetworkStats;->size:I
+Landroid/net/NetworkStats;->tag:[I
+Landroid/net/NetworkStats;->txBytes:[J
+Landroid/net/NetworkStats;->txPackets:[J
+Landroid/net/NetworkStats;->uid:[I
Landroid/net/SSLCertificateSocketFactory;->getHttpSocketFactory(ILandroid/net/SSLSessionCache;)Lorg/apache/http/conn/ssl/SSLSocketFactory;
+Landroid/net/TrafficStats;->getStatsService()Landroid/net/INetworkStatsService;
Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I
Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;
Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
@@ -534,7 +564,9 @@
Landroid/os/UserManager;->getUserHandle(I)I
Landroid/os/UserManager;->getUserInfo(I)Landroid/content/pm/UserInfo;
Landroid/os/UserManager;->getUserSerialNumber(I)I
+Landroid/os/UserManager;->getUsers()Ljava/util/List;
Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
+Landroid/os/UserManager;->isLinkedUser()Z
Landroid/os/UserManager;->isUserUnlocked(I)Z
Landroid/os/WorkSource;->add(ILjava/lang/String;)Z
Landroid/os/WorkSource;->add(I)Z
@@ -627,6 +659,7 @@
Landroid/telephony/SubscriptionManager;->getPhoneId(I)I
Landroid/telephony/SubscriptionManager;->getSubId(I)[I
Landroid/telephony/SubscriptionManager;->setDefaultSmsSubId(I)V
+Landroid/telephony/TelephonyManager;->checkCarrierPrivilegesForPackage(Ljava/lang/String;)I
Landroid/telephony/TelephonyManager;->from(Landroid/content/Context;)Landroid/telephony/TelephonyManager;
Landroid/telephony/TelephonyManager;->getCurrentPhoneType()I
Landroid/telephony/TelephonyManager;->getCurrentPhoneType(I)I
@@ -636,10 +669,12 @@
Landroid/telephony/TelephonyManager;->getITelephony()Lcom/android/internal/telephony/ITelephony;
Landroid/telephony/TelephonyManager;->getLine1Number(I)Ljava/lang/String;
Landroid/telephony/TelephonyManager;->getNetworkClass(I)I
+Landroid/telephony/TelephonyManager;->getNetworkCountryIso(I)Ljava/lang/String;
Landroid/telephony/TelephonyManager;->getNetworkOperator(I)Ljava/lang/String;
Landroid/telephony/TelephonyManager;->getNetworkOperatorName(I)Ljava/lang/String;
Landroid/telephony/TelephonyManager;->getNetworkType(I)I
Landroid/telephony/TelephonyManager;->getSimOperator(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getSimOperatorName(I)Ljava/lang/String;
Landroid/telephony/TelephonyManager;->getSimSerialNumber(I)Ljava/lang/String;
Landroid/telephony/TelephonyManager;->getSubscriberId(I)Ljava/lang/String;
Landroid/telephony/TelephonyManager;->isMultiSimEnabled()Z
@@ -711,6 +746,14 @@
Landroid/view/SurfaceView;->mFormat:I
Landroid/view/SurfaceView;->mRequestedFormat:I
Landroid/view/SurfaceView;->mSurfaceHolder:Landroid/view/SurfaceHolder;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker;-><init>(Landroid/content/Context;I)V
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker;->logEvent(Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;)V
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionAction(III)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionAction(IIILandroid/view/textclassifier/TextClassification;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(II)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(IILandroid/view/textclassifier/TextClassification;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(IILandroid/view/textclassifier/TextSelection;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionStarted(I)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
Landroid/view/textservice/TextServicesManager;->isSpellCheckerEnabled()Z
Landroid/view/TextureView;->mLayer:Landroid/view/HardwareLayer;
Landroid/view/TextureView;->mSurface:Landroid/graphics/SurfaceTexture;
@@ -718,6 +761,7 @@
Landroid/view/TouchDelegate;->mDelegateTargeted:Z
Landroid/view/VelocityTracker;->obtain(Ljava/lang/String;)Landroid/view/VelocityTracker;
Landroid/view/View;->clearAccessibilityFocus()V
+Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z
Landroid/view/View;->computeOpaqueFlags()V
Landroid/view/ViewConfiguration;->mFadingMarqueeEnabled:Z
Landroid/view/ViewConfiguration;->sHasPermanentMenuKeySet:Z
@@ -732,6 +776,7 @@
Landroid/view/View;->getViewRootImpl()Landroid/view/ViewRootImpl;
Landroid/view/ViewGroup;->FLAG_SUPPORT_STATIC_TRANSFORMATIONS:I
Landroid/view/ViewGroup;->FLAG_USE_CHILD_DRAWING_ORDER:I
+Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V
Landroid/view/ViewGroup$MarginLayoutParams;->endMargin:I
Landroid/view/ViewGroup$MarginLayoutParams;->startMargin:I
Landroid/view/ViewGroup;->mChildren:[Landroid/view/View;
@@ -810,6 +855,7 @@
Landroid/webkit/WebView;->enablePlatformNotifications()V
Landroid/webkit/WebViewFactory;->getLoadedPackageInfo()Landroid/content/pm/PackageInfo;
Landroid/webkit/WebViewFactory;->getProvider()Landroid/webkit/WebViewFactoryProvider;
+Landroid/webkit/WebViewFactory;->sPackageInfo:Landroid/content/pm/PackageInfo;
Landroid/webkit/WebView;->getVisibleTitleHeight()I
Landroid/webkit/WebView;->getWebViewProvider()Landroid/webkit/WebViewProvider;
Landroid/webkit/WebView;->isPaused()Z
@@ -939,6 +985,7 @@
Landroid/widget/Switch;->mThumbDrawable:Landroid/graphics/drawable/Drawable;
Landroid/widget/Switch;->mTrackDrawable:Landroid/graphics/drawable/Drawable;
Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V
+Landroid/widget/TextView;->assumeLayout()V
Landroid/widget/TextView;->createEditorIfNeeded()V
Landroid/widget/TextView;->mCursorDrawableRes:I
Landroid/widget/TextView;->mCurTextColor:I
@@ -977,6 +1024,8 @@
Lcom/android/internal/os/BatteryStatsImpl$Uid$Sensor;->getHandle()I
Lcom/android/internal/os/BatteryStatsImpl$Uid$Sensor;->getSensorTime()Lcom/android/internal/os/BatteryStatsImpl$Timer;
Lcom/android/internal/os/BatteryStatsImpl$Uid$Wakelock;->getWakeTime(I)Lcom/android/internal/os/BatteryStatsImpl$Timer;
+Lcom/android/internal/os/FuseAppLoop;->onCommand(IJJJI[B)V
+Lcom/android/internal/os/FuseAppLoop;->onOpen(JJ)[B
Lcom/android/internal/os/PowerProfile;->getAveragePower(Ljava/lang/String;)D
Lcom/android/internal/os/PowerProfile;->getAveragePower(Ljava/lang/String;I)D
Lcom/android/internal/os/PowerProfile;->getBatteryCapacity()D
@@ -1038,8 +1087,13 @@
Lcom/android/internal/view/menu/MenuItemImpl;->setMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V
Lcom/android/okhttp/ConnectionPool;->maxIdleConnections:I
Lcom/android/okhttp/ConnectionPool;->systemDefault:Lcom/android/okhttp/ConnectionPool;
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setAlpnProtocols([B)V
Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String;
Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList;
+Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard;
+Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V
+Ldalvik/system/CloseGuard;->warnIfOpen()V
Ldalvik/system/DexFile;->mCookie:Ljava/lang/Object;
Ldalvik/system/DexFile;->mFileName:Ljava/lang/String;
Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
@@ -1050,6 +1104,7 @@
Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
Ldalvik/system/DexPathList;->nativeLibraryDirectories:Ljava/util/List;
Ldalvik/system/DexPathList;->nativeLibraryPathElements:[Ldalvik/system/DexPathList$NativeLibraryElement;
+Ldalvik/system/DexPathList;->systemNativeLibraryDirectories:Ljava/util/List;
Ldalvik/system/VMDebug;->dumpReferenceTables()V
Ldalvik/system/VMRuntime;->clearGrowthLimit()V
Ldalvik/system/VMRuntime;->getCurrentInstructionSet()Ljava/lang/String;
@@ -1074,6 +1129,7 @@
Ljava/lang/Class;->dexClassDefIndex:I
Ljava/lang/ClassLoader;->parent:Ljava/lang/ClassLoader;
Ljava/lang/Daemons$Daemon;->stop()V
+Ljava/lang/Daemons$Daemon;->thread:Ljava/lang/Thread;
Ljava/lang/Daemons$FinalizerDaemon;->finalizingObject:Ljava/lang/Object;
Ljava/lang/Daemons$FinalizerDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerDaemon;
Ljava/lang/Daemons$FinalizerWatchdogDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerWatchdogDaemon;
@@ -1092,6 +1148,8 @@
Ljava/net/Socket;->impl:Ljava/net/SocketImpl;
Ljava/net/URI;->host:Ljava/lang/String;
Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
+Ljava/security/spec/ECParameterSpec;->getCurveName()Ljava/lang/String;
+Ljava/security/spec/ECParameterSpec;->setCurveName(Ljava/lang/String;)V
Ljava/util/ArrayList$SubList;->parent:Ljava/util/AbstractList;
Ljava/util/ArrayList$SubList;->parentOffset:I
Ljava/util/ArrayList$SubList;->size:I
@@ -1101,5 +1159,6 @@
Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
+Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>(Ljavax/net/ssl/SSLSocketFactory;)V
Lorg/json/JSONArray;->values:Ljava/util/List;
Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index fdb0ac9..04c44a3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -425,6 +425,12 @@
* safely called after {@link #onPause()} and allows and application to safely
* wait until {@link #onStop()} to save persistent state.</p>
*
+ * <p class="note">For applications targeting platforms starting with
+ * {@link android.os.Build.VERSION_CODES#P} {@link #onSaveInstanceState(Bundle)}
+ * will always be called after {@link #onStop}, so an application may safely
+ * perform fragment transactions in {@link #onStop} and will be able to save
+ * persistent state later.</p>
+ *
* <p>For those methods that are not marked as being killable, the activity's
* process will not be killed by the system starting from the time the method
* is called and continuing after it returns. Thus an activity is in the killable
@@ -1577,8 +1583,11 @@
* call through to the default implementation, otherwise be prepared to save
* all of the state of each view yourself.
*
- * <p>If called, this method will occur before {@link #onStop}. There are
- * no guarantees about whether it will occur before or after {@link #onPause}.
+ * <p>If called, this method will occur after {@link #onStop} for applications
+ * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}.
+ * For applications targeting earlier platform versions this method will occur
+ * before {@link #onStop} and there are no guarantees about whether it will
+ * occur before or after {@link #onPause}.
*
* @param outState Bundle in which to place your saved state.
*
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 29e091b..42825f0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -488,12 +488,14 @@
}
}
- public boolean isPreHoneycomb() {
- if (activity != null) {
- return activity.getApplicationInfo().targetSdkVersion
- < android.os.Build.VERSION_CODES.HONEYCOMB;
- }
- return false;
+ private boolean isPreHoneycomb() {
+ return activity != null && activity.getApplicationInfo().targetSdkVersion
+ < android.os.Build.VERSION_CODES.HONEYCOMB;
+ }
+
+ private boolean isPreP() {
+ return activity != null && activity.getApplicationInfo().targetSdkVersion
+ < android.os.Build.VERSION_CODES.P;
}
public boolean isPersistable() {
@@ -4165,9 +4167,12 @@
* {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call.
*/
private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
+ // Before P onSaveInstanceState was called before onStop, starting with P it's
+ // called after. Before Honeycomb state was always saved before onPause.
final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
&& !r.isPreHoneycomb();
- if (shouldSaveState) {
+ final boolean isPreP = r.isPreP();
+ if (shouldSaveState && isPreP) {
callActivityOnSaveInstanceState(r);
}
@@ -4186,6 +4191,10 @@
r.setState(ON_STOP);
EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
r.activity.getComponentName().getClassName(), reason);
+
+ if (shouldSaveState && !isPreP) {
+ callActivityOnSaveInstanceState(r);
+ }
}
private void updateVisibility(ActivityClientRecord r, boolean show) {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index f21746c..39bccc3 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -51,6 +51,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.DeadSystemException;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -1329,11 +1330,7 @@
private void copyStreamToWallpaperFile(InputStream data, FileOutputStream fos)
throws IOException {
- byte[] buffer = new byte[32768];
- int amt;
- while ((amt=data.read(buffer)) > 0) {
- fos.write(buffer, 0, amt);
- }
+ FileUtils.copy(data, fos);
}
/**
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 4461b16..38d9025 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -31,4 +31,7 @@
SliceSpec[] getPinnedSpecs(in Uri uri, String pkg);
int checkSlicePermission(in Uri uri, String pkg, int pid, int uid);
void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
+
+ byte[] getBackupPayload(int user);
+ void applyRestore(in byte[] payload, int user);
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index e5f3eae..3f13fff 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -20,9 +20,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.IContentProvider;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.net.Uri;
@@ -193,10 +193,15 @@
* <p>
* Pinned state is not persisted across reboots, so apps are expected to re-pin any slices
* they still care about after a reboot.
+ * <p>
+ * This may only be called by apps that are the default launcher for the device
+ * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
*
* @param uri The uri of the slice being pinned.
* @param specs The list of supported {@link SliceSpec}s of the callback.
* @see SliceProvider#onSlicePinned(Uri)
+ * @see Intent#ACTION_ASSIST
+ * @see Intent#CATEGORY_HOME
*/
public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
try {
@@ -211,10 +216,15 @@
* Remove a pin for a slice.
* <p>
* If the slice has no other pins/callbacks then the slice will be unpinned.
+ * <p>
+ * This may only be called by apps that are the default launcher for the device
+ * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
*
* @param uri The uri of the slice being unpinned.
* @see #pinSlice
* @see SliceProvider#onSliceUnpinned(Uri)
+ * @see Intent#ACTION_ASSIST
+ * @see Intent#CATEGORY_HOME
*/
public void unpinSlice(@NonNull Uri uri) {
try {
@@ -262,17 +272,13 @@
*/
public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) {
ContentResolver resolver = mContext.getContentResolver();
- IContentProvider provider = resolver.acquireProvider(uri);
- try {
+ try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
- final Bundle res = provider.call(resolver.getPackageName(),
- SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
+ final Bundle res = provider.call(SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
} catch (RemoteException e) {
Log.e(TAG, "Unable to get slice descendants", e);
- } finally {
- resolver.releaseProvider(provider);
}
return Collections.emptyList();
}
@@ -288,17 +294,15 @@
public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(uri, "uri");
ContentResolver resolver = mContext.getContentResolver();
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
+ try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(mContext.getPackageName(), SliceProvider.METHOD_SLICE,
- null, extras);
+ final Bundle res = provider.call(SliceProvider.METHOD_SLICE, null, extras);
Bundle.setDefusable(res, true);
if (res == null) {
return null;
@@ -308,8 +312,6 @@
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
- } finally {
- resolver.releaseProvider(provider);
}
}
@@ -344,15 +346,13 @@
String authority = providers.get(0).providerInfo.authority;
Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority).build();
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
+ try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
- final Bundle res = provider.call(mContext.getPackageName(),
- SliceProvider.METHOD_MAP_ONLY_INTENT, null, extras);
+ final Bundle res = provider.call(SliceProvider.METHOD_MAP_ONLY_INTENT, null, extras);
if (res == null) {
return null;
}
@@ -361,8 +361,6 @@
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
- } finally {
- resolver.releaseProvider(provider);
}
}
@@ -399,17 +397,15 @@
String authority = providers.get(0).providerInfo.authority;
Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority).build();
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
+ try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(mContext.getPackageName(),
- SliceProvider.METHOD_MAP_INTENT, null, extras);
+ final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
if (res == null) {
return null;
}
@@ -418,8 +414,6 @@
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
- } finally {
- resolver.releaseProvider(provider);
}
}
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
new file mode 100644
index 0000000..c811999
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -0,0 +1,184 @@
+/*
+ * 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.hardware.biometrics;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.os.CancellationSignal;
+import android.os.Parcelable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This is the common interface that all biometric authentication classes should implement.
+ * @hide
+ */
+public interface BiometricAuthenticator {
+
+ /**
+ * Container for biometric data
+ * @hide
+ */
+ abstract class BiometricIdentifier implements Parcelable {}
+
+ /**
+ * Container for callback data from {@link BiometricAuthenticator#authenticate(
+ * CancellationSignal, Executor, AuthenticationCallback)} and
+ * {@link BiometricAuthenticator#authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)}
+ */
+ class AuthenticationResult {
+ private BiometricIdentifier mIdentifier;
+ private CryptoObject mCryptoObject;
+ private int mUserId;
+
+ /**
+ * @hide
+ */
+ public AuthenticationResult() { }
+
+ /**
+ * Authentication result
+ * @param crypto
+ * @param identifier
+ * @param userId
+ * @hide
+ */
+ public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+ int userId) {
+ mCryptoObject = crypto;
+ mIdentifier = identifier;
+ mUserId = userId;
+ }
+
+ /**
+ * Obtain the crypto object associated with this transaction
+ * @return crypto object provided to {@link BiometricAuthenticator#authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}
+ */
+ public CryptoObject getCryptoObject() {
+ return mCryptoObject;
+ }
+
+ /**
+ * Obtain the biometric identifier associated with this operation. Applications are strongly
+ * discouraged from associating specific identifiers with specific applications or
+ * operations.
+ * @hide
+ */
+ public BiometricIdentifier getId() {
+ return mIdentifier;
+ }
+
+ /**
+ * Obtain the userId for which this biometric was authenticated.
+ * @hide
+ */
+ public int getUserId() {
+ return mUserId;
+ }
+ };
+
+ /**
+ * Callback structure provided to {@link BiometricAuthenticator#authenticate(CancellationSignal,
+ * Executor, AuthenticationCallback)} or {@link BiometricAuthenticator#authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}. Users must provide
+ * an implementation of this for listening to biometric events.
+ */
+ abstract class AuthenticationCallback {
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further actions will be made on this object.
+ * @param errorCode An integer identifying the error message
+ * @param errString A human-readable error string that can be shown on an UI
+ */
+ public void onAuthenticationError(int errorCode, CharSequence errString) {}
+
+ /**
+ * Called when a recoverable error has been encountered during authentication. The help
+ * string is provided to give the user guidance for what went wrong, such as "Sensor dirty,
+ * please clean it."
+ * @param helpCode An integer identifying the error message
+ * @param helpString A human-readable string that can be shown on an UI
+ */
+ public void onAuthenticationHelp(int helpCode, CharSequence helpString) {}
+
+ /**
+ * Called when a biometric is recognized.
+ * @param result An object containing authentication-related data
+ */
+ public void onAuthenticationSucceeded(AuthenticationResult result) {}
+
+ /**
+ * Called when a biometric is valid but not recognized.
+ */
+ public void onAuthenticationFailed() {}
+
+ /**
+ * Called when a biometric has been acquired, but hasn't been processed yet.
+ * @hide
+ */
+ public void onAuthenticationAcquired(int acquireInfo) {}
+ };
+
+ /**
+ * This call warms up the hardware and starts scanning for valid biometrics. It terminates
+ * when {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)} is called, at which point the crypto object becomes invalid. This
+ * operation can be canceled by using the provided cancel object. The application wil receive
+ * authentication errors through {@link AuthenticationCallback}. Calling
+ * {@link BiometricAuthenticator#authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)} while an existing authentication attempt is occurring will stop
+ * the previous client and start a new authentication. The interrupted client will receive a
+ * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)}.
+ *
+ * @throws IllegalArgumentException If any of the arguments are null
+ *
+ * @param crypto Object associated with the call
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ */
+ void authenticate(@NonNull CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback);
+
+ /**
+ * This call warms up the hardware and starts scanning for valid biometrics. It terminates
+ * when {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)} is called. This operation can be canceled by using the provided cancel
+ * object. The application wil receive authentication errors through
+ * {@link AuthenticationCallback}. Calling {@link BiometricAuthenticator#authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)} while an existing
+ * authentication attempt is occurring will stop the previous client and start a new
+ * authentication. The interrupted client will receive a cancelled notification through
+ * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+ *
+ * @throws IllegalArgumentException If any of the arguments are null
+ *
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ */
+ void authenticate(@NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback);
+}
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
new file mode 100644
index 0000000..638f525
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -0,0 +1,168 @@
+/*
+ * 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.hardware.biometrics;
+
+import android.hardware.fingerprint.FingerprintManager;
+
+/**
+ * Interface containing all of the fingerprint-specific constants.
+ * @hide
+ */
+public interface BiometricFingerprintConstants {
+ //
+ // Error messages from fingerprint hardware during initilization, enrollment, authentication or
+ // removal. Must agree with the list in fingerprint.h
+ //
+
+ /**
+ * The hardware is unavailable. Try again later.
+ */
+ public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
+
+ /**
+ * Error state returned when the sensor was unable to process the current image.
+ */
+ public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
+
+ /**
+ * Error state returned when the current request has been running too long. This is intended to
+ * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
+ * platform and sensor-specific, but is generally on the order of 30 seconds.
+ */
+ public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
+
+ /**
+ * Error state returned for operations like enrollment; the operation cannot be completed
+ * because there's not enough storage remaining to complete the operation.
+ */
+ public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+
+ /**
+ * The operation was canceled because the fingerprint sensor is unavailable. For example,
+ * this may happen when the user is switched, the device is locked or another pending operation
+ * prevents or disables it.
+ */
+ public static final int FINGERPRINT_ERROR_CANCELED = 5;
+
+ /**
+ * The {@link FingerprintManager#remove} call failed. Typically this will happen when the
+ * provided fingerprint id was incorrect.
+ *
+ * @hide
+ */
+ public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
+
+ /**
+ * The operation was canceled because the API is locked out due to too many attempts.
+ * This occurs after 5 failed attempts, and lasts for 30 seconds.
+ */
+ public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
+
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ * These messages are typically reserved for internal operations such as enrollment, but may be
+ * used to express vendor errors not covered by the ones in fingerprint.h. Applications are
+ * expected to show the error message string if they happen, but are advised not to rely on the
+ * message id since they will be device and vendor-specific
+ */
+ public static final int FINGERPRINT_ERROR_VENDOR = 8;
+
+ /**
+ * The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times.
+ * Fingerprint authentication is disabled until the user unlocks with strong authentication
+ * (PIN/Pattern/Password)
+ */
+ public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
+
+ /**
+ * The user canceled the operation. Upon receiving this, applications should use alternate
+ * authentication (e.g. a password). The application should also provide the means to return
+ * to fingerprint authentication, such as a "use fingerprint" button.
+ */
+ public static final int FINGERPRINT_ERROR_USER_CANCELED = 10;
+
+ /**
+ * The user does not have any fingerprints enrolled.
+ */
+ public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11;
+
+ /**
+ * The device does not have a fingerprint sensor.
+ */
+ public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12;
+
+ /**
+ * @hide
+ */
+ public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
+
+ //
+ // Image acquisition messages. Must agree with those in fingerprint.h
+ //
+
+ /**
+ * The image acquired was good.
+ */
+ public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+
+ /**
+ * Only a partial fingerprint image was detected. During enrollment, the user should be
+ * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
+ */
+ public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
+
+ /**
+ * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
+ * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
+ */
+ public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
+
+ /**
+ * The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
+ * For example, it's reasonable return this after multiple
+ * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor
+ * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
+ * when this is returned.
+ */
+ public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
+
+ /**
+ * The fingerprint image was unreadable due to lack of motion. This is most appropriate for
+ * linear array sensors that require a swipe motion.
+ */
+ public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
+
+ /**
+ * The fingerprint image was incomplete due to quick motion. While mostly appropriate for
+ * linear array sensors, this could also happen if the finger was moved during acquisition.
+ * The user should be asked to move the finger slower (linear) or leave the finger on the sensor
+ * longer.
+ */
+ public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
+
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ * @hide
+ */
+ public static final int FINGERPRINT_ACQUIRED_VENDOR = 6;
+ /**
+ * @hide
+ */
+ public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
+}
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
new file mode 100644
index 0000000..496d9c5
--- /dev/null
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -0,0 +1,79 @@
+/*
+ * 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.hardware.biometrics;
+
+import android.annotation.NonNull;
+import android.security.keystore.AndroidKeyStoreProvider;
+
+import java.security.Signature;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+/**
+ * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
+ * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ * @hide
+ */
+public class CryptoObject {
+ private final Object mCrypto;
+
+ public CryptoObject(@NonNull Signature signature) {
+ mCrypto = signature;
+ }
+
+ public CryptoObject(@NonNull Cipher cipher) {
+ mCrypto = cipher;
+ }
+
+ public CryptoObject(@NonNull Mac mac) {
+ mCrypto = mac;
+ }
+
+ /**
+ * Get {@link Signature} object.
+ * @return {@link Signature} object or null if this doesn't contain one.
+ */
+ public Signature getSignature() {
+ return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+ }
+
+ /**
+ * Get {@link Cipher} object.
+ * @return {@link Cipher} object or null if this doesn't contain one.
+ */
+ public Cipher getCipher() {
+ return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+ }
+
+ /**
+ * Get {@link Mac} object.
+ * @return {@link Mac} object or null if this doesn't contain one.
+ */
+ public Mac getMac() {
+ return mCrypto instanceof Mac ? (Mac) mCrypto : null;
+ }
+
+ /**
+ * @hide
+ * @return the opId associated with this object or 0 if none
+ */
+ public final long getOpId() {
+ return mCrypto != null
+ ? AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
+ }
+};
diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
index 41be397..00f3c36 100644
--- a/core/java/android/hardware/display/AmbientBrightnessDayStats.java
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
@@ -17,6 +17,8 @@
package android.hardware.display;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,11 +30,12 @@
/**
* AmbientBrightnessDayStats stores and manipulates brightness stats over a single day.
* {@see DisplayManager.getAmbientBrightnessStats()}
- * TODO: Make this system API
*
* @hide
*/
-public class AmbientBrightnessDayStats implements Parcelable {
+@SystemApi
+@TestApi
+public final class AmbientBrightnessDayStats implements Parcelable {
/** The localdate for which brightness stats are being tracked */
private final LocalDate mLocalDate;
@@ -48,30 +51,30 @@
*/
public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
@NonNull float[] bucketBoundaries) {
- Preconditions.checkNotNull(localDate);
- Preconditions.checkNotNull(bucketBoundaries);
- int numBuckets = bucketBoundaries.length;
- if (numBuckets < 1) {
- throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value");
- }
- mLocalDate = localDate;
- mBucketBoundaries = bucketBoundaries;
- mStats = new float[numBuckets];
+ this(localDate, bucketBoundaries, null);
}
/**
* @hide
*/
public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
- @NonNull float[] bucketBoundaries, @NonNull float[] stats) {
+ @NonNull float[] bucketBoundaries, float[] stats) {
Preconditions.checkNotNull(localDate);
Preconditions.checkNotNull(bucketBoundaries);
- Preconditions.checkNotNull(stats);
+ Preconditions.checkArrayElementsInRange(bucketBoundaries, 0, Float.MAX_VALUE,
+ "bucketBoundaries");
if (bucketBoundaries.length < 1) {
throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value");
}
- if (bucketBoundaries.length != stats.length) {
- throw new IllegalArgumentException("Bucket boundaries and stats must be of same size.");
+ checkSorted(bucketBoundaries);
+ if (stats == null) {
+ stats = new float[bucketBoundaries.length];
+ } else {
+ Preconditions.checkArrayElementsInRange(stats, 0, Float.MAX_VALUE, "stats");
+ if (bucketBoundaries.length != stats.length) {
+ throw new IllegalArgumentException(
+ "Bucket boundaries and stats must be of same size.");
+ }
}
mLocalDate = localDate;
mBucketBoundaries = bucketBoundaries;
@@ -193,4 +196,16 @@
}
return low;
}
+
+ private static void checkSorted(float[] values) {
+ if (values.length <= 1) {
+ return;
+ }
+ float prevValue = values[0];
+ for (int i = 1; i < values.length; i++) {
+ Preconditions.checkState(prevValue < values[i]);
+ prevValue = values[i];
+ }
+ return;
+ }
}
diff --git a/core/java/android/hardware/fingerprint/Fingerprint.java b/core/java/android/hardware/fingerprint/Fingerprint.java
index c307634..c7ce8fa 100644
--- a/core/java/android/hardware/fingerprint/Fingerprint.java
+++ b/core/java/android/hardware/fingerprint/Fingerprint.java
@@ -15,6 +15,7 @@
*/
package android.hardware.fingerprint;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.os.Parcel;
import android.os.Parcelable;
@@ -22,7 +23,7 @@
* Container for fingerprint metadata.
* @hide
*/
-public final class Fingerprint implements Parcelable {
+public final class Fingerprint extends BiometricAuthenticator.BiometricIdentifier {
private CharSequence mName;
private int mGroupId;
private int mFingerId;
diff --git a/core/java/android/hardware/fingerprint/FingerprintDialog.java b/core/java/android/hardware/fingerprint/FingerprintDialog.java
index 6b7fab7..49835963 100644
--- a/core/java/android/hardware/fingerprint/FingerprintDialog.java
+++ b/core/java/android/hardware/fingerprint/FingerprintDialog.java
@@ -23,19 +23,25 @@
import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.DialogInterface;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
+import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.CryptoObject;
import android.hardware.fingerprint.IFingerprintDialogReceiver;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.text.TextUtils;
+import java.security.Signature;
import java.util.concurrent.Executor;
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
/**
* A class that manages a system-provided fingerprint dialog.
*/
-public class FingerprintDialog {
+public class FingerprintDialog implements BiometricAuthenticator, BiometricFingerprintConstants {
/**
* @hide
@@ -200,6 +206,7 @@
}
}
+ private PackageManager mPackageManager;
private FingerprintManager mFingerprintManager;
private Bundle mBundle;
private ButtonInfo mPositiveButtonInfo;
@@ -227,37 +234,209 @@
mPositiveButtonInfo = positiveButtonInfo;
mNegativeButtonInfo = negativeButtonInfo;
mFingerprintManager = context.getSystemService(FingerprintManager.class);
+ mPackageManager = context.getPackageManager();
}
/**
+ * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
+ * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ */
+ public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
+ public CryptoObject(@NonNull Signature signature) {
+ super(signature);
+ }
+
+ public CryptoObject(@NonNull Cipher cipher) {
+ super(cipher);
+ }
+
+ public CryptoObject(@NonNull Mac mac) {
+ super(mac);
+ }
+
+ /**
+ * Get {@link Signature} object.
+ * @return {@link Signature} object or null if this doesn't contain one.
+ */
+ public Signature getSignature() {
+ return super.getSignature();
+ }
+
+ /**
+ * Get {@link Cipher} object.
+ * @return {@link Cipher} object or null if this doesn't contain one.
+ */
+ public Cipher getCipher() {
+ return super.getCipher();
+ }
+
+ /**
+ * Get {@link Mac} object.
+ * @return {@link Mac} object or null if this doesn't contain one.
+ */
+ public Mac getMac() {
+ return super.getMac();
+ }
+ }
+
+ /**
+ * Container for callback data from {@link #authenticate(
+ * CancellationSignal, Executor, AuthenticationCallback)} and
+ * {@link #authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)}
+ */
+ public static class AuthenticationResult extends BiometricAuthenticator.AuthenticationResult {
+ /**
+ * Authentication result
+ * @param crypto
+ * @param identifier
+ * @param userId
+ * @hide
+ */
+ public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+ int userId) {
+ super(crypto, identifier, userId);
+ }
+ /**
+ * Obtain the crypto object associated with this transaction
+ * @return crypto object provided to {@link #authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}
+ */
+ public CryptoObject getCryptoObject() {
+ return (CryptoObject) super.getCryptoObject();
+ }
+ }
+
+ /**
+ * Callback structure provided to {@link FingerprintDialog#authenticate(CancellationSignal,
+ * Executor, AuthenticationCallback)} or {@link FingerprintDialog#authenticate(CryptoObject,
+ * CancellationSignal, Executor, AuthenticationCallback)}. Users must provide an implementation
+ * of this for listening to authentication events.
+ */
+ public static abstract class AuthenticationCallback extends
+ BiometricAuthenticator.AuthenticationCallback {
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further actions will be made on this object.
+ * @param errorCode An integer identifying the error message
+ * @param errString A human-readable error string that can be shown on an UI
+ */
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {}
+
+ /**
+ * Called when a recoverable error has been encountered during authentication. The help
+ * string is provided to give the user guidance for what went wrong, such as "Sensor dirty,
+ * please clean it."
+ * @param helpCode An integer identifying the error message
+ * @param helpString A human-readable string that can be shown on an UI
+ */
+ @Override
+ public void onAuthenticationHelp(int helpCode, CharSequence helpString) {}
+
+ /**
+ * Called when a biometric is recognized.
+ * @param result An object containing authentication-related data
+ */
+ public void onAuthenticationSucceeded(AuthenticationResult result) {}
+
+ /**
+ * Called when a biometric is valid but not recognized.
+ */
+ @Override
+ public void onAuthenticationFailed() {}
+
+ /**
+ * Called when a biometric has been acquired, but hasn't been processed yet.
+ * @hide
+ */
+ @Override
+ public void onAuthenticationAcquired(int acquireInfo) {}
+
+ /**
+ * @param result An object containing authentication-related data
+ * @hide
+ */
+ @Override
+ public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
+ onAuthenticationSucceeded(new AuthenticationResult(
+ (CryptoObject) result.getCryptoObject(),
+ result.getId(),
+ result.getUserId()));
+ }
+ }
+
+
+ /**
+ * @param crypto Object associated with the call
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ * @hide
+ */
+ @Override
+ public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+ if (!(callback instanceof FingerprintDialog.AuthenticationCallback)) {
+ throw new IllegalArgumentException("Callback cannot be casted");
+ }
+ authenticate(crypto, cancel, executor, (AuthenticationCallback) callback);
+ }
+
+ /**
+ *
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ * @hide
+ */
+ @Override
+ public void authenticate(@NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+ if (!(callback instanceof FingerprintDialog.AuthenticationCallback)) {
+ throw new IllegalArgumentException("Callback cannot be casted");
+ }
+ authenticate(cancel, executor, (AuthenticationCallback) callback);
+ }
+
+
+ /**
* This call warms up the fingerprint hardware, displays a system-provided dialog,
* and starts scanning for a fingerprint. It terminates when
- * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when
- * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called,
- * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user
- * dismisses the system-provided dialog, at which point the crypto object becomes invalid.
- * This operation can be canceled by using the provided cancel object. The application will
- * receive authentication errors through {@link AuthenticationCallback}, and button events
- * through the corresponding callback set in
- * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
- * It is safe to reuse the {@link FingerprintDialog} object, and calling
- * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
- * while an existing authentication attempt is occurring will stop the previous client and
- * start a new authentication. The interrupted client will receive a cancelled notification
- * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+ * {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called, when
+ * {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)}, or when the user dismisses the system-provided dialog, at which point
+ * the crypto object becomes invalid. This operation can be canceled by using the provided
+ * cancel object. The application will receive authentication errors through
+ * {@link AuthenticationCallback}, and button events through the
+ * corresponding callback set in {@link Builder#setNegativeButton(CharSequence,
+ * Executor, DialogInterface.OnClickListener)}. It is safe to reuse the
+ * {@link FingerprintDialog} object, and calling {@link FingerprintDialog#authenticate(
+ * CancellationSignal, Executor, AuthenticationCallback)} while an
+ * existing authentication attempt is occurring will stop the previous client and start a
+ * new authentication. The interrupted client will receive a cancelled notification through
+ * {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)}.
*
- * @throws IllegalArgumentException if any of the arguments are null
+ * @throws IllegalArgumentException If any of the arguments are null
*
- * @param crypto object associated with the call
- * @param cancel an object that can be used to cancel authentication
- * @param executor an executor to handle callback events
- * @param callback an object to receive authentication events
+ * @param crypto Object associated with the call
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
*/
@RequiresPermission(USE_FINGERPRINT)
- public void authenticate(@NonNull FingerprintManager.CryptoObject crypto,
+ public void authenticate(@NonNull CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
- @NonNull FingerprintManager.AuthenticationCallback callback) {
+ @NonNull AuthenticationCallback callback) {
+ if (handlePreAuthenticationErrors(callback, executor)) {
+ return;
+ }
mFingerprintManager.authenticate(crypto, cancel, mBundle, executor, mDialogReceiver,
callback);
}
@@ -265,29 +444,57 @@
/**
* This call warms up the fingerprint hardware, displays a system-provided dialog,
* and starts scanning for a fingerprint. It terminates when
- * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when
- * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called,
- * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user
- * dismisses the system-provided dialog. This operation can be canceled by using the provided
- * cancel object. The application will receive authentication errors through
- * {@link AuthenticationCallback}, and button events through the corresponding callback set in
+ * {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called, when
+ * {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)} is called, or when the user dismisses the system-provided dialog.
+ * This operation can be canceled by using the provided cancel object. The application will
+ * receive authentication errors through {@link AuthenticationCallback},
+ * and button events through the corresponding callback set in
* {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
* It is safe to reuse the {@link FingerprintDialog} object, and calling
- * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
- * while an existing authentication attempt is occurring will stop the previous client and
- * start a new authentication. The interrupted client will receive a cancelled notification
- * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+ * {@link FingerprintDialog#authenticate(CancellationSignal, Executor,
+ * AuthenticationCallback)} while an existing authentication attempt is
+ * occurring will stop the previous client and start a new authentication. The interrupted
+ * client will receive a cancelled notification through
+ * {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)}.
*
- * @throws IllegalArgumentException if any of the arguments are null
+ * @throws IllegalArgumentException If any of the arguments are null
*
- * @param cancel an object that can be used to cancel authentication
- * @param executor an executor to handle callback events
- * @param callback an object to receive authentication events
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
*/
@RequiresPermission(USE_FINGERPRINT)
public void authenticate(@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
- @NonNull FingerprintManager.AuthenticationCallback callback) {
+ @NonNull AuthenticationCallback callback) {
+ if (handlePreAuthenticationErrors(callback, executor)) {
+ return;
+ }
mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback);
}
+
+ private boolean handlePreAuthenticationErrors(AuthenticationCallback callback,
+ Executor executor) {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ sendError(FINGERPRINT_ERROR_HW_NOT_PRESENT, callback, executor);
+ return true;
+ } else if (!mFingerprintManager.isHardwareDetected()) {
+ sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, callback, executor);
+ return true;
+ } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
+ sendError(FINGERPRINT_ERROR_NO_FINGERPRINTS, callback, executor);
+ return true;
+ }
+ return false;
+ }
+
+ private void sendError(int error, AuthenticationCallback callback, Executor executor) {
+ executor.execute(() -> {
+ callback.onAuthenticationError(error, mFingerprintManager.getErrorString(
+ error, 0 /* vendorCode */));
+ });
+ }
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 62d92c4..2dfe673 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -27,6 +27,8 @@
import android.annotation.SystemService;
import android.app.ActivityManager;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricFingerprintConstants;
import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -38,7 +40,6 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.security.keystore.AndroidKeyStoreProvider;
import android.util.Log;
import android.util.Slog;
@@ -51,9 +52,14 @@
/**
* A class that coordinates access to the fingerprint hardware.
+ * @deprecated See {@link FingerprintDialog} which shows a system-provided dialog upon starting
+ * authentication. In a world where devices may have in-display fingerprint sensors, it's much
+ * more realistic to have a system-provided authentication dialog since the in-display sensor
+ * location may vary by vendor/device.
*/
+@Deprecated
@SystemService(Context.FINGERPRINT_SERVICE)
-public class FingerprintManager {
+public class FingerprintManager implements BiometricFingerprintConstants {
private static final String TAG = "FingerprintManager";
private static final boolean DEBUG = true;
private static final int MSG_ENROLL_RESULT = 100;
@@ -64,147 +70,14 @@
private static final int MSG_REMOVED = 105;
private static final int MSG_ENUMERATED = 106;
- //
- // Error messages from fingerprint hardware during initilization, enrollment, authentication or
- // removal. Must agree with the list in fingerprint.h
- //
-
- /**
- * The hardware is unavailable. Try again later.
- */
- public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
-
- /**
- * Error state returned when the sensor was unable to process the current image.
- */
- public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
-
- /**
- * Error state returned when the current request has been running too long. This is intended to
- * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
- * platform and sensor-specific, but is generally on the order of 30 seconds.
- */
- public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
-
- /**
- * Error state returned for operations like enrollment; the operation cannot be completed
- * because there's not enough storage remaining to complete the operation.
- */
- public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
-
- /**
- * The operation was canceled because the fingerprint sensor is unavailable. For example,
- * this may happen when the user is switched, the device is locked or another pending operation
- * prevents or disables it.
- */
- public static final int FINGERPRINT_ERROR_CANCELED = 5;
-
- /**
- * The {@link FingerprintManager#remove} call failed. Typically this will happen when the
- * provided fingerprint id was incorrect.
- *
- * @hide
- */
- public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
-
- /**
- * The operation was canceled because the API is locked out due to too many attempts.
- * This occurs after 5 failed attempts, and lasts for 30 seconds.
- */
- public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
-
- /**
- * Hardware vendors may extend this list if there are conditions that do not fall under one of
- * the above categories. Vendors are responsible for providing error strings for these errors.
- * These messages are typically reserved for internal operations such as enrollment, but may be
- * used to express vendor errors not covered by the ones in fingerprint.h. Applications are
- * expected to show the error message string if they happen, but are advised not to rely on the
- * message id since they will be device and vendor-specific
- */
- public static final int FINGERPRINT_ERROR_VENDOR = 8;
-
- /**
- * The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times.
- * Fingerprint authentication is disabled until the user unlocks with strong authentication
- * (PIN/Pattern/Password)
- */
- public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
-
- /**
- * The user canceled the operation. Upon receiving this, applications should use alternate
- * authentication (e.g. a password). The application should also provide the means to return
- * to fingerprint authentication, such as a "use fingerprint" button.
- */
- public static final int FINGERPRINT_ERROR_USER_CANCELED = 10;
-
- /**
- * @hide
- */
- public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
-
- //
- // Image acquisition messages. Must agree with those in fingerprint.h
- //
-
- /**
- * The image acquired was good.
- */
- public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
-
- /**
- * Only a partial fingerprint image was detected. During enrollment, the user should be
- * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
- */
- public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
-
- /**
- * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
- * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
- */
- public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
-
- /**
- * The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
- * For example, it's reasonable return this after multiple
- * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor
- * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
- * when this is returned.
- */
- public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
-
- /**
- * The fingerprint image was unreadable due to lack of motion. This is most appropriate for
- * linear array sensors that require a swipe motion.
- */
- public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
-
- /**
- * The fingerprint image was incomplete due to quick motion. While mostly appropriate for
- * linear array sensors, this could also happen if the finger was moved during acquisition.
- * The user should be asked to move the finger slower (linear) or leave the finger on the sensor
- * longer.
- */
- public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
-
- /**
- * Hardware vendors may extend this list if there are conditions that do not fall under one of
- * the above categories. Vendors are responsible for providing error strings for these errors.
- * @hide
- */
- public static final int FINGERPRINT_ACQUIRED_VENDOR = 6;
- /**
- * @hide
- */
- public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
-
private IFingerprintService mService;
private Context mContext;
private IBinder mToken = new Binder();
- private AuthenticationCallback mAuthenticationCallback;
+ private BiometricAuthenticator.AuthenticationCallback mAuthenticationCallback;
private EnrollmentCallback mEnrollmentCallback;
private RemovalCallback mRemovalCallback;
private EnumerateCallback mEnumerateCallback;
- private CryptoObject mCryptoObject;
+ private android.hardware.biometrics.CryptoObject mCryptoObject;
private Fingerprint mRemovalFingerprint;
private Handler mHandler;
private Executor mExecutor;
@@ -217,9 +90,9 @@
}
private class OnAuthenticationCancelListener implements OnCancelListener {
- private CryptoObject mCrypto;
+ private android.hardware.biometrics.CryptoObject mCrypto;
- public OnAuthenticationCancelListener(CryptoObject crypto) {
+ public OnAuthenticationCancelListener(android.hardware.biometrics.CryptoObject crypto) {
mCrypto = crypto;
}
@@ -233,18 +106,17 @@
* A wrapper class for the crypto objects supported by FingerprintManager. Currently the
* framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
*/
- public static final class CryptoObject {
-
+ public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
public CryptoObject(@NonNull Signature signature) {
- mCrypto = signature;
+ super(signature);
}
public CryptoObject(@NonNull Cipher cipher) {
- mCrypto = cipher;
+ super(cipher);
}
public CryptoObject(@NonNull Mac mac) {
- mCrypto = mac;
+ super(mac);
}
/**
@@ -252,7 +124,7 @@
* @return {@link Signature} object or null if this doesn't contain one.
*/
public Signature getSignature() {
- return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+ return super.getSignature();
}
/**
@@ -260,7 +132,7 @@
* @return {@link Cipher} object or null if this doesn't contain one.
*/
public Cipher getCipher() {
- return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+ return super.getCipher();
}
/**
@@ -268,20 +140,9 @@
* @return {@link Mac} object or null if this doesn't contain one.
*/
public Mac getMac() {
- return mCrypto instanceof Mac ? (Mac) mCrypto : null;
+ return super.getMac();
}
-
- /**
- * @hide
- * @return the opId associated with this object or 0 if none
- */
- public long getOpId() {
- return mCrypto != null ?
- AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
- }
-
- private final Object mCrypto;
- };
+ }
/**
* Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
@@ -334,13 +195,15 @@
* int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
* fingerprint events.
*/
- public static abstract class AuthenticationCallback {
+ public static abstract class AuthenticationCallback
+ extends BiometricAuthenticator.AuthenticationCallback {
/**
* Called when an unrecoverable error has been encountered and the operation is complete.
* No further callbacks will be made on this object.
* @param errorCode An integer identifying the error message
* @param errString A human-readable error string that can be shown in UI
*/
+ @Override
public void onAuthenticationError(int errorCode, CharSequence errString) { }
/**
@@ -350,6 +213,7 @@
* @param helpCode An integer identifying the error message
* @param helpString A human-readable string that can be shown in UI
*/
+ @Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
/**
@@ -361,6 +225,7 @@
/**
* Called when a fingerprint is valid but not recognized.
*/
+ @Override
public void onAuthenticationFailed() { }
/**
@@ -369,7 +234,19 @@
* @param acquireInfo one of FINGERPRINT_ACQUIRED_* constants
* @hide
*/
+ @Override
public void onAuthenticationAcquired(int acquireInfo) {}
+
+ /**
+ * @hide
+ * @param result
+ */
+ @Override
+ public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
+ onAuthenticationSucceeded(new AuthenticationResult(
+ (CryptoObject) result.getCryptoObject(),
+ (Fingerprint) result.getId(), result.getUserId()));
+ }
};
/**
@@ -489,7 +366,12 @@
* by <a href="{@docRoot}training/articles/keystore.html">Android Keystore
* facility</a>.
* @throws IllegalStateException if the crypto primitive is not initialized.
+ * @deprecated See {@link FingerprintDialog#authenticate(CancellationSignal, Executor,
+ * FingerprintDialog.AuthenticationCallback)} and {@link FingerprintDialog#authenticate(
+ * FingerprintDialog.CryptoObject, CancellationSignal, Executor,
+ * FingerprintDialog.AuthenticationCallback)}
*/
+ @Deprecated
@RequiresPermission(USE_FINGERPRINT)
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
@@ -554,12 +436,12 @@
* @param userId the user ID that the fingerprint hardware will authenticate for.
*/
private void authenticate(int userId,
- @Nullable CryptoObject crypto,
+ @Nullable android.hardware.biometrics.CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
@NonNull IFingerprintDialogReceiver receiver,
- @NonNull AuthenticationCallback callback) {
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
mCryptoObject = crypto;
if (cancel.isCanceled()) {
Log.w(TAG, "authentication already canceled");
@@ -598,7 +480,7 @@
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
@NonNull IFingerprintDialogReceiver receiver,
- @NonNull AuthenticationCallback callback) {
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
if (cancel == null) {
throw new IllegalArgumentException("Must supply a cancellation signal");
}
@@ -626,12 +508,12 @@
* @param callback
* @hide
*/
- public void authenticate(@NonNull CryptoObject crypto,
+ public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
@NonNull IFingerprintDialogReceiver receiver,
- @NonNull AuthenticationCallback callback) {
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
if (crypto == null) {
throw new IllegalArgumentException("Must supply a crypto object");
}
@@ -648,9 +530,10 @@
throw new IllegalArgumentException("Must supply a receiver");
}
if (callback == null) {
- throw new IllegalArgumentException("Must supply a calback");
+ throw new IllegalArgumentException("Must supply a callback");
}
- authenticate(UserHandle.myUserId(), crypto, cancel, bundle, executor, receiver, callback);
+ authenticate(UserHandle.myUserId(), crypto, cancel,
+ bundle, executor, receiver, callback);
}
/**
@@ -848,7 +731,10 @@
* Determine if there is at least one fingerprint enrolled.
*
* @return true if at least one fingerprint is enrolled, false otherwise
+ * @deprecated See {@link FingerprintDialog} and
+ * {@link FingerprintDialog#FINGERPRINT_ERROR_NO_FINGERPRINTS}
*/
+ @Deprecated
@RequiresPermission(USE_FINGERPRINT)
public boolean hasEnrolledFingerprints() {
if (mService != null) try {
@@ -879,7 +765,10 @@
* Determine if fingerprint hardware is present and functional.
*
* @return true if hardware is present and functional, false otherwise.
+ * @deprecated See {@link FingerprintDialog} and
+ * {@link FingerprintDialog#FINGERPRINT_ERROR_HW_UNAVAILABLE}
*/
+ @Deprecated
@RequiresPermission(USE_FINGERPRINT)
public boolean isHardwareDetected() {
if (mService != null) {
@@ -1049,8 +938,8 @@
private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
if (mAuthenticationCallback != null) {
- final AuthenticationResult result =
- new AuthenticationResult(mCryptoObject, fp, userId);
+ final BiometricAuthenticator.AuthenticationResult result =
+ new BiometricAuthenticator.AuthenticationResult(mCryptoObject, fp, userId);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
@@ -1126,7 +1015,7 @@
}
}
- private void cancelAuthentication(CryptoObject cryptoObject) {
+ private void cancelAuthentication(android.hardware.biometrics.CryptoObject cryptoObject) {
if (mService != null) try {
mService.cancelAuthentication(mToken, mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1160,6 +1049,12 @@
case FINGERPRINT_ERROR_USER_CANCELED:
return mContext.getString(
com.android.internal.R.string.fingerprint_error_user_canceled);
+ case FINGERPRINT_ERROR_NO_FINGERPRINTS:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_no_fingerprints);
+ case FINGERPRINT_ERROR_HW_NOT_PRESENT:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_hw_not_present);
case FINGERPRINT_ERROR_VENDOR: {
String[] msgArray = mContext.getResources().getStringArray(
com.android.internal.R.array.fingerprint_error_vendor);
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index bda720bb..7922276 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -439,6 +439,10 @@
}
}
+ private static long addIfSupported(long stat) {
+ return (stat == UNSUPPORTED) ? 0 : stat;
+ }
+
/**
* Return number of packets transmitted across mobile networks since device
* boot. Counts packets across all mobile network interfaces, and always
@@ -451,7 +455,7 @@
public static long getMobileTxPackets() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getTxPackets(iface);
+ total += addIfSupported(getTxPackets(iface));
}
return total;
}
@@ -468,7 +472,7 @@
public static long getMobileRxPackets() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getRxPackets(iface);
+ total += addIfSupported(getRxPackets(iface));
}
return total;
}
@@ -485,7 +489,7 @@
public static long getMobileTxBytes() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getTxBytes(iface);
+ total += addIfSupported(getTxBytes(iface));
}
return total;
}
@@ -502,7 +506,7 @@
public static long getMobileRxBytes() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getRxBytes(iface);
+ total += addIfSupported(getRxBytes(iface));
}
return total;
}
@@ -517,9 +521,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- if (stat != UNSUPPORTED) {
- total += stat;
- }
+ total += addIfSupported(stat);
}
return total;
}
@@ -534,9 +536,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- if (stat != UNSUPPORTED) {
- total += stat;
- }
+ total += addIfSupported(stat);
}
return total;
}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 9edcc0e..5ca3a41 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -720,6 +720,10 @@
LOOP: while (end < length) {
switch (uriString.charAt(end)) {
case '/': // Start of path
+ case '\\':// Start of path
+ // Per http://url.spec.whatwg.org/#host-state, the \ character
+ // is treated as if it were a / character when encountered in a
+ // host
case '?': // Start of query
case '#': // Start of fragment
break LOOP;
@@ -758,6 +762,10 @@
case '#': // Start of fragment
return ""; // Empty path.
case '/': // Start of path!
+ case '\\':// Start of path!
+ // Per http://url.spec.whatwg.org/#host-state, the \ character
+ // is treated as if it were a / character when encountered in a
+ // host
break LOOP;
}
pathStart++;
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 7c53ec1..1160415 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,6 +16,11 @@
package android.os;
+import static android.system.OsConstants.SPLICE_F_MORE;
+import static android.system.OsConstants.SPLICE_F_MOVE;
+import static android.system.OsConstants.S_ISFIFO;
+import static android.system.OsConstants.S_ISREG;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.provider.DocumentsContract.Document;
@@ -28,7 +33,9 @@
import android.webkit.MimeTypeMap;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.SizedInputStream;
+import libcore.io.IoUtils;
import libcore.util.EmptyArray;
import java.io.BufferedInputStream;
@@ -41,10 +48,12 @@
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
@@ -81,6 +90,14 @@
private static final File[] EMPTY = new File[0];
+ private static final boolean ENABLE_COPY_OPTIMIZATIONS = true;
+
+ private static final long COPY_CHECKPOINT_BYTES = 524288;
+
+ public interface ProgressListener {
+ public void onProgress(long progress);
+ }
+
/**
* Set owner and mode of of given {@link File}.
*
@@ -185,6 +202,9 @@
return false;
}
+ /**
+ * @deprecated use {@link #copy(File, File)} instead.
+ */
@Deprecated
public static boolean copyFile(File srcFile, File destFile) {
try {
@@ -195,14 +215,19 @@
}
}
- // copy a file from srcFile to destFile, return true if succeed, return
- // false if fail
+ /**
+ * @deprecated use {@link #copy(File, File)} instead.
+ */
+ @Deprecated
public static void copyFileOrThrow(File srcFile, File destFile) throws IOException {
try (InputStream in = new FileInputStream(srcFile)) {
copyToFileOrThrow(in, destFile);
}
}
+ /**
+ * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+ */
@Deprecated
public static boolean copyToFile(InputStream inputStream, File destFile) {
try {
@@ -214,32 +239,250 @@
}
/**
- * Copy data from a source stream to destFile.
- * Return true if succeed, return false if failed.
+ * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
*/
- public static void copyToFileOrThrow(InputStream inputStream, File destFile)
- throws IOException {
+ @Deprecated
+ public static void copyToFileOrThrow(InputStream in, File destFile) throws IOException {
if (destFile.exists()) {
destFile.delete();
}
- FileOutputStream out = new FileOutputStream(destFile);
- try {
- byte[] buffer = new byte[4096];
- int bytesRead;
- while ((bytesRead = inputStream.read(buffer)) >= 0) {
- out.write(buffer, 0, bytesRead);
- }
- } finally {
- out.flush();
+ try (FileOutputStream out = new FileOutputStream(destFile)) {
+ copy(in, out);
try {
- out.getFD().sync();
- } catch (IOException e) {
+ Os.fsync(out.getFD());
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
}
- out.close();
}
}
/**
+ * Copy the contents of one file to another, replacing any existing content.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull File from, @NonNull File to) throws IOException {
+ return copy(from, to, null, null);
+ }
+
+ /**
+ * Copy the contents of one file to another, replacing any existing content.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @param listener to be periodically notified as the copy progresses.
+ * @param signal to signal if the copy should be cancelled early.
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull File from, @NonNull File to,
+ @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
+ throws IOException {
+ try (FileInputStream in = new FileInputStream(from);
+ FileOutputStream out = new FileOutputStream(to)) {
+ return copy(in, out, listener, signal);
+ }
+ }
+
+ /**
+ * Copy the contents of one stream to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull InputStream in, @NonNull OutputStream out) throws IOException {
+ return copy(in, out, null, null);
+ }
+
+ /**
+ * Copy the contents of one stream to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @param listener to be periodically notified as the copy progresses.
+ * @param signal to signal if the copy should be cancelled early.
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull InputStream in, @NonNull OutputStream out,
+ @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
+ throws IOException {
+ if (ENABLE_COPY_OPTIMIZATIONS) {
+ if (in instanceof FileInputStream && out instanceof FileOutputStream) {
+ return copy(((FileInputStream) in).getFD(), ((FileOutputStream) out).getFD(),
+ listener, signal);
+ }
+ }
+
+ // Worse case fallback to userspace
+ return copyInternalUserspace(in, out, listener, signal);
+ }
+
+ /**
+ * Copy the contents of one FD to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out)
+ throws IOException {
+ return copy(in, out, null, null);
+ }
+
+ /**
+ * Copy the contents of one FD to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @param listener to be periodically notified as the copy progresses.
+ * @param signal to signal if the copy should be cancelled early.
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
+ @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
+ throws IOException {
+ return copy(in, out, listener, signal, Long.MAX_VALUE);
+ }
+
+ /**
+ * Copy the contents of one FD to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @param listener to be periodically notified as the copy progresses.
+ * @param signal to signal if the copy should be cancelled early.
+ * @param count the number of bytes to copy.
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
+ @Nullable ProgressListener listener, @Nullable CancellationSignal signal, long count)
+ throws IOException {
+ if (ENABLE_COPY_OPTIMIZATIONS) {
+ try {
+ final StructStat st_in = Os.fstat(in);
+ final StructStat st_out = Os.fstat(out);
+ if (S_ISREG(st_in.st_mode) && S_ISREG(st_out.st_mode)) {
+ return copyInternalSendfile(in, out, listener, signal, count);
+ } else if (S_ISFIFO(st_in.st_mode) || S_ISFIFO(st_out.st_mode)) {
+ return copyInternalSplice(in, out, listener, signal, count);
+ }
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ }
+
+ // Worse case fallback to userspace
+ return copyInternalUserspace(in, out, listener, signal, count);
+ }
+
+ /**
+ * Requires one of input or output to be a pipe.
+ */
+ @VisibleForTesting
+ public static long copyInternalSplice(FileDescriptor in, FileDescriptor out,
+ ProgressListener listener, CancellationSignal signal, long count)
+ throws ErrnoException {
+ long progress = 0;
+ long checkpoint = 0;
+
+ long t;
+ while ((t = Os.splice(in, null, out, null, Math.min(count, COPY_CHECKPOINT_BYTES),
+ SPLICE_F_MOVE | SPLICE_F_MORE)) != 0) {
+ progress += t;
+ checkpoint += t;
+ count -= t;
+
+ if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+ if (signal != null) {
+ signal.throwIfCanceled();
+ }
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
+ checkpoint = 0;
+ }
+ }
+ return progress;
+ }
+
+ /**
+ * Requires both input and output to be a regular file.
+ */
+ @VisibleForTesting
+ public static long copyInternalSendfile(FileDescriptor in, FileDescriptor out,
+ ProgressListener listener, CancellationSignal signal, long count)
+ throws ErrnoException {
+ long progress = 0;
+ long checkpoint = 0;
+
+ long t;
+ while ((t = Os.sendfile(out, in, null, Math.min(count, COPY_CHECKPOINT_BYTES))) != 0) {
+ progress += t;
+ checkpoint += t;
+ count -= t;
+
+ if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+ if (signal != null) {
+ signal.throwIfCanceled();
+ }
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
+ checkpoint = 0;
+ }
+ }
+ return progress;
+ }
+
+ @VisibleForTesting
+ public static long copyInternalUserspace(FileDescriptor in, FileDescriptor out,
+ ProgressListener listener, CancellationSignal signal, long count) throws IOException {
+ if (count != Long.MAX_VALUE) {
+ return copyInternalUserspace(new SizedInputStream(new FileInputStream(in), count),
+ new FileOutputStream(out), listener, signal);
+ } else {
+ return copyInternalUserspace(new FileInputStream(in),
+ new FileOutputStream(out), listener, signal);
+ }
+ }
+
+ @VisibleForTesting
+ public static long copyInternalUserspace(InputStream in, OutputStream out,
+ ProgressListener listener, CancellationSignal signal) throws IOException {
+ long progress = 0;
+ long checkpoint = 0;
+ byte[] buffer = new byte[8192];
+
+ int t;
+ while ((t = in.read(buffer)) != -1) {
+ out.write(buffer, 0, t);
+
+ progress += t;
+ checkpoint += t;
+
+ if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+ if (signal != null) {
+ signal.throwIfCanceled();
+ }
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
+ checkpoint = 0;
+ }
+ }
+ return progress;
+ }
+
+ /**
* Check if a filename is "safe" (no metacharacters or spaces).
* @param file The file to check
*/
@@ -797,4 +1040,69 @@
}
return val * pow;
}
+
+ @VisibleForTesting
+ public static class MemoryPipe extends Thread implements AutoCloseable {
+ private final FileDescriptor[] pipe;
+ private final byte[] data;
+ private final boolean sink;
+
+ private MemoryPipe(byte[] data, boolean sink) throws IOException {
+ try {
+ this.pipe = Os.pipe();
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ this.data = data;
+ this.sink = sink;
+ }
+
+ private MemoryPipe startInternal() {
+ super.start();
+ return this;
+ }
+
+ public static MemoryPipe createSource(byte[] data) throws IOException {
+ return new MemoryPipe(data, false).startInternal();
+ }
+
+ public static MemoryPipe createSink(byte[] data) throws IOException {
+ return new MemoryPipe(data, true).startInternal();
+ }
+
+ public FileDescriptor getFD() {
+ return sink ? pipe[1] : pipe[0];
+ }
+
+ public FileDescriptor getInternalFD() {
+ return sink ? pipe[0] : pipe[1];
+ }
+
+ @Override
+ public void run() {
+ final FileDescriptor fd = getInternalFD();
+ try {
+ int i = 0;
+ while (i < data.length) {
+ if (sink) {
+ i += Os.read(fd, data, i, data.length - i);
+ } else {
+ i += Os.write(fd, data, i, data.length - i);
+ }
+ }
+ } catch (IOException | ErrnoException e) {
+ // Ignored
+ } finally {
+ if (sink) {
+ SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
+ }
+ IoUtils.closeQuietly(fd);
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ IoUtils.closeQuietly(getFD());
+ }
+ }
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b2b43cf..2093cec 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -393,8 +393,9 @@
public static final String DISALLOW_CONFIG_VPN = "no_config_vpn";
/**
- * Specifies if a user is disallowed from configuring location mode. Device owner and profile
- * owners can set this restriction and it only applies on the managed user.
+ * Specifies if a user is disallowed from enabling or disabling location providers. As a
+ * result, user is disallowed from turning on or off location. Device owner and profile owners
+ * can set this restriction and it only applies on the managed user.
*
* <p>In a managed profile, location sharing is forced off when it's off on primary user, so
* user can still turn off location sharing on managed profile when the restriction is set by
@@ -408,11 +409,12 @@
*
* <p>Key for user restrictions.
* <p>Type: Boolean
+ * @see android.location.LocationManager#isProviderEnabled(String)
* @see DevicePolicyManager#addUserRestriction(ComponentName, String)
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
- public static final String DISALLOW_CONFIG_LOCATION_MODE = "no_config_location_mode";
+ public static final String DISALLOW_CONFIG_LOCATION = "no_config_location";
/**
* Specifies if date, time and timezone configuring is disallowed.
diff --git a/core/java/android/print/PrintFileDocumentAdapter.java b/core/java/android/print/PrintFileDocumentAdapter.java
index 747400d..a5f9305 100644
--- a/core/java/android/print/PrintFileDocumentAdapter.java
+++ b/core/java/android/print/PrintFileDocumentAdapter.java
@@ -21,6 +21,8 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignal.OnCancelListener;
+import android.os.FileUtils;
+import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -114,28 +116,15 @@
@Override
protected Void doInBackground(Void... params) {
- InputStream in = null;
- OutputStream out = new FileOutputStream(mDestination.getFileDescriptor());
- final byte[] buffer = new byte[8192];
- try {
- in = new FileInputStream(mFile);
- while (true) {
- if (isCancelled()) {
- break;
- }
- final int readByteCount = in.read(buffer);
- if (readByteCount < 0) {
- break;
- }
- out.write(buffer, 0, readByteCount);
- }
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error writing data!", ioe);
- mResultCallback.onWriteFailed(mContext.getString(
- R.string.write_fail_reason_cannot_write));
- } finally {
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(out);
+ try (InputStream in = new FileInputStream(mFile);
+ OutputStream out = new FileOutputStream(mDestination.getFileDescriptor())) {
+ FileUtils.copy(in, out, null, mCancellationSignal);
+ } catch (OperationCanceledException e) {
+ // Ignored; already handled below
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Error writing data!", e);
+ mResultCallback.onWriteFailed(mContext.getString(
+ R.string.write_fail_reason_cannot_write));
}
return null;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f59afc9..e3b8a10 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1903,7 +1903,7 @@
cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
String newValue = getStringForUser(cr, name, userHandle);
StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newValue, prevValue, tag,
- makeDefault ? 1 : 0, userHandle);
+ makeDefault, userHandle);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
return false;
@@ -10739,14 +10739,14 @@
public static final String LOW_POWER_MODE = "low_power";
/**
- * Battery level [1-99] at which low power mode automatically turns on.
+ * Battery level [1-100] at which low power mode automatically turns on.
* If 0, it will not automatically turn on.
* @hide
*/
public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
private static final Validator LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 99);
+ new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
/**
* If not 0, the activity manager will aggressively finish activities and
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 4e4a037..7cd08f7 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -26,9 +26,13 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.security.KeyStore;
+import android.security.keystore.AndroidKeyStoreProvider;
import com.android.internal.widget.ILockSettings;
+import java.security.Key;
+import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
@@ -113,9 +117,11 @@
private final ILockSettings mBinder;
+ private final KeyStore mKeyStore;
- private RecoveryController(ILockSettings binder) {
+ private RecoveryController(ILockSettings binder, KeyStore keystore) {
mBinder = binder;
+ mKeyStore = keystore;
}
/**
@@ -133,7 +139,7 @@
public static RecoveryController getInstance(Context context) {
ILockSettings lockSettings =
ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
- return new RecoveryController(lockSettings);
+ return new RecoveryController(lockSettings, KeyStore.getInstance());
}
/**
@@ -430,6 +436,7 @@
}
/**
+ * Deprecated.
* Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable
* key store. Returns the raw material of the key.
*
@@ -444,7 +451,6 @@
public byte[] generateAndStoreKey(@NonNull String alias, byte[] account)
throws InternalRecoveryServiceException, LockScreenRequiredException {
try {
- // TODO: add account
return mBinder.generateAndStoreKey(alias);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -457,6 +463,72 @@
}
/**
+ * Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable
+ * key store. Returns {@link javax.crypto.SecretKey}.
+ *
+ * @param alias The key alias.
+ * @param account The account associated with the key.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ * @throws LockScreenRequiredException if the user has not set a lock screen. This is required
+ * to generate recoverable keys, as the snapshots are encrypted using a key derived from the
+ * lock screen.
+ * @hide
+ */
+ public Key generateKey(@NonNull String alias, byte[] account)
+ throws InternalRecoveryServiceException, LockScreenRequiredException {
+ // TODO: update RecoverySession.recoverKeys
+ try {
+ String grantAlias = mBinder.generateKey(alias, account);
+ if (grantAlias == null) {
+ return null;
+ }
+ Key result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
+ mKeyStore,
+ grantAlias,
+ KeyStore.UID_SELF);
+ return result;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (UnrecoverableKeyException e) {
+ throw new InternalRecoveryServiceException("Access to newly generated key failed for");
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ERROR_INSECURE_USER) {
+ throw new LockScreenRequiredException(e.getMessage());
+ }
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Gets a key called {@code alias} from the recoverable key store.
+ *
+ * @param alias The key alias.
+ * @return The key.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ * @throws UnrecoverableKeyException if key is permanently invalidated or not found.
+ * @hide
+ */
+ public @Nullable Key getKey(@NonNull String alias)
+ throws InternalRecoveryServiceException, UnrecoverableKeyException {
+ try {
+ String grantAlias = mBinder.getKey(alias);
+ if (grantAlias == null) {
+ return null;
+ }
+ return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
+ mKeyStore,
+ grantAlias,
+ KeyStore.UID_SELF);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
* Removes a key called {@code alias} from the recoverable key store.
*
* @param alias The key alias.
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 23ae4b9..d66322c 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -17,6 +17,7 @@
package android.service.notification;
import android.app.ActivityManager;
+import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.content.ComponentName;
@@ -1445,4 +1446,99 @@
return !config.allowAlarms && !config.allowMediaSystemOther
&& areAllPriorityOnlyNotificationZenSoundsMuted(config);
}
+
+ /**
+ * Returns a description of the current do not disturb settings from config.
+ * - If turned on manually and end time is known, returns end time.
+ * - If turned on by an automatic rule, returns the automatic rule name.
+ * - If on due to an app, returns the app name.
+ * - If there's a combination of rules/apps that trigger, then shows the one that will
+ * last the longest if applicable.
+ * @return null if do not disturb is off.
+ */
+ public static String getDescription(Context context, boolean zenOn, ZenModeConfig config) {
+ if (!zenOn) {
+ return null;
+ }
+
+ String secondaryText = "";
+ long latestEndTime = -1;
+
+ // DND turned on by manual rule
+ if (config.manualRule != null) {
+ final Uri id = config.manualRule.conditionId;
+ if (config.manualRule.enabler != null) {
+ // app triggered manual rule
+ String appName = getOwnerCaption(context, config.manualRule.enabler);
+ if (!appName.isEmpty()) {
+ secondaryText = appName;
+ }
+ } else {
+ if (id == null) {
+ // Do not disturb manually triggered to remain on forever until turned off
+ // No subtext
+ return null;
+ } else {
+ latestEndTime = tryParseCountdownConditionId(id);
+ if (latestEndTime > 0) {
+ final CharSequence formattedTime = getFormattedTime(context,
+ latestEndTime, isToday(latestEndTime),
+ context.getUserId());
+ secondaryText = context.getString(R.string.zen_mode_until, formattedTime);
+ }
+ }
+ }
+ }
+
+ // DND turned on by an automatic rule
+ for (ZenRule automaticRule : config.automaticRules.values()) {
+ if (automaticRule.isAutomaticActive()) {
+ if (isValidEventConditionId(automaticRule.conditionId)
+ || isValidScheduleConditionId(automaticRule.conditionId)) {
+ // set text if automatic rule end time is the latest active rule end time
+ long endTime = parseAutomaticRuleEndTime(context, automaticRule.conditionId);
+ if (endTime > latestEndTime) {
+ latestEndTime = endTime;
+ secondaryText = automaticRule.name;
+ }
+ } else {
+ // set text if 3rd party rule
+ return automaticRule.name;
+ }
+ }
+ }
+
+ return !secondaryText.equals("") ? secondaryText : null;
+ }
+
+ private static long parseAutomaticRuleEndTime(Context context, Uri id) {
+ if (isValidEventConditionId(id)) {
+ // cannot look up end times for events
+ return Long.MAX_VALUE;
+ }
+
+ if (isValidScheduleConditionId(id)) {
+ ScheduleCalendar schedule = toScheduleCalendar(id);
+ long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis());
+
+ // check if automatic rule will end on next alarm
+ if (schedule.exitAtAlarm()) {
+ long nextAlarm = getNextAlarm(context);
+ schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm);
+ if (schedule.shouldExitForAlarm(endTimeMs)) {
+ return nextAlarm;
+ }
+ }
+
+ return endTimeMs;
+ }
+
+ return -1;
+ }
+
+ private static long getNextAlarm(Context context) {
+ final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ final AlarmManager.AlarmClockInfo info = alarms.getNextAlarmClock(context.getUserId());
+ return info != null ? info.getTriggerTime() : 0;
+ }
}
diff --git a/core/java/android/service/settings/suggestions/Suggestion.java b/core/java/android/service/settings/suggestions/Suggestion.java
index 11e1e67..e97f963a 100644
--- a/core/java/android/service/settings/suggestions/Suggestion.java
+++ b/core/java/android/service/settings/suggestions/Suggestion.java
@@ -40,6 +40,7 @@
*/
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_HAS_BUTTON,
+ FLAG_ICON_TINTABLE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Flags {
@@ -49,6 +50,10 @@
* Flag for suggestion type with a single button
*/
public static final int FLAG_HAS_BUTTON = 1 << 0;
+ /**
+ * @hide
+ */
+ public static final int FLAG_ICON_TINTABLE = 1 << 1;
private final String mId;
private final CharSequence mTitle;
diff --git a/core/java/android/text/style/DrawableMarginSpan.java b/core/java/android/text/style/DrawableMarginSpan.java
index 3524179..cd199b3 100644
--- a/core/java/android/text/style/DrawableMarginSpan.java
+++ b/core/java/android/text/style/DrawableMarginSpan.java
@@ -16,60 +16,100 @@
package android.text.style;
+import android.annotation.NonNull;
+import android.annotation.Px;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.Spanned;
-public class DrawableMarginSpan
-implements LeadingMarginSpan, LineHeightSpan
-{
- public DrawableMarginSpan(Drawable b) {
- mDrawable = b;
+/**
+ * A span which adds a drawable and a padding to the paragraph it's attached to.
+ * <p>
+ * If the height of the drawable is bigger than the height of the line it's attached to then the
+ * line height is increased to fit the drawable. <code>DrawableMarginSpan</code> allows setting a
+ * padding between the drawable and the text. The default value is 0. The span must be set from the
+ * beginning of the text, otherwise either the span won't be rendered or it will be rendered
+ * incorrectly.
+ * <p>
+ * For example, a drawable and a padding of 20px can be added like this:
+ * <pre>{@code SpannableString string = new SpannableString("Text with a drawable.");
+ * string.setSpan(new DrawableMarginSpan(drawable, 20), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/drawablemarginspan.png" />
+ * <figcaption>Text with a drawable and a padding.</figcaption>
+ * <p>
+ *
+ * @see IconMarginSpan for working with a {@link android.graphics.Bitmap} instead of
+ * a {@link Drawable}.
+ */
+public class DrawableMarginSpan implements LeadingMarginSpan, LineHeightSpan {
+ private static final int STANDARD_PAD_WIDTH = 0;
+
+ @NonNull
+ private final Drawable mDrawable;
+ @Px
+ private final int mPad;
+
+ /**
+ * Creates a {@link DrawableMarginSpan} from a {@link Drawable}. The pad width will be 0.
+ *
+ * @param drawable the drawable to be added
+ */
+ public DrawableMarginSpan(@NonNull Drawable drawable) {
+ this(drawable, STANDARD_PAD_WIDTH);
}
- public DrawableMarginSpan(Drawable b, int pad) {
- mDrawable = b;
+ /**
+ * Creates a {@link DrawableMarginSpan} from a {@link Drawable} and a padding, in pixels.
+ *
+ * @param drawable the drawable to be added
+ * @param pad the distance between the drawable and the text
+ */
+ public DrawableMarginSpan(@NonNull Drawable drawable, int pad) {
+ mDrawable = drawable;
mPad = pad;
}
+ @Override
public int getLeadingMargin(boolean first) {
return mDrawable.getIntrinsicWidth() + mPad;
}
- public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
- int top, int baseline, int bottom,
- CharSequence text, int start, int end,
- boolean first, Layout layout) {
+ @Override
+ public void drawLeadingMargin(@NonNull Canvas c, @NonNull Paint p, int x, int dir,
+ int top, int baseline, int bottom,
+ @NonNull CharSequence text, int start, int end,
+ boolean first, @NonNull Layout layout) {
int st = ((Spanned) text).getSpanStart(this);
- int ix = (int)x;
- int itop = (int)layout.getLineTop(layout.getLineForOffset(st));
+ int ix = (int) x;
+ int itop = (int) layout.getLineTop(layout.getLineForOffset(st));
int dw = mDrawable.getIntrinsicWidth();
int dh = mDrawable.getIntrinsicHeight();
// XXX What to do about Paint?
- mDrawable.setBounds(ix, itop, ix+dw, itop+dh);
+ mDrawable.setBounds(ix, itop, ix + dw, itop + dh);
mDrawable.draw(c);
}
- public void chooseHeight(CharSequence text, int start, int end,
- int istartv, int v,
- Paint.FontMetricsInt fm) {
+ @Override
+ public void chooseHeight(@NonNull CharSequence text, int start, int end,
+ int istartv, int v,
+ @NonNull Paint.FontMetricsInt fm) {
if (end == ((Spanned) text).getSpanEnd(this)) {
int ht = mDrawable.getIntrinsicHeight();
int need = ht - (v + fm.descent - fm.ascent - istartv);
- if (need > 0)
+ if (need > 0) {
fm.descent += need;
+ }
need = ht - (v + fm.bottom - fm.top - istartv);
- if (need > 0)
+ if (need > 0) {
fm.bottom += need;
+ }
}
}
-
- private Drawable mDrawable;
- private int mPad;
}
diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java
index 5b8a6dd..1b16f33 100644
--- a/core/java/android/text/style/DynamicDrawableSpan.java
+++ b/core/java/android/text/style/DynamicDrawableSpan.java
@@ -16,6 +16,9 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -24,32 +27,71 @@
import java.lang.ref.WeakReference;
/**
+ * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with
+ * the bottom or with the baseline of the surrounding text.
+ * <p>
+ * For an implementation that constructs the drawable from various sources (<code>Bitmap</code>,
+ * <code>Drawable</code>, resource id or <code>Uri</code>) use {@link ImageSpan}.
+ * <p>
+ * A simple implementation of <code>DynamicDrawableSpan</code> that uses drawables from resources
+ * looks like this:
+ * <pre>
+ * class MyDynamicDrawableSpan extends DynamicDrawableSpan {
*
+ * private final Context mContext;
+ * private final int mResourceId;
+ *
+ * public MyDynamicDrawableSpan(Context context, @DrawableRes int resourceId) {
+ * mContext = context;
+ * mResourceId = resourceId;
+ * }
+ *
+ * {@literal @}Override
+ * public Drawable getDrawable() {
+ * Drawable drawable = mContext.getDrawable(mResourceId);
+ * drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ * return drawable;
+ * }
+ * }</pre>
+ * The class can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with a drawable span");
+ * string.setSpan(new MyDynamicDrawableSpan(context, R.mipmap.ic_launcher), 12, 20, Spanned
+ * .SPAN_EXCLUSIVE_EXCLUSIVE);</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/dynamicdrawablespan.png" />
+ * <figcaption>Replacing text with a drawable.</figcaption>
*/
public abstract class DynamicDrawableSpan extends ReplacementSpan {
- private static final String TAG = "DynamicDrawableSpan";
-
+
/**
* A constant indicating that the bottom of this span should be aligned
* with the bottom of the surrounding text, i.e., at the same level as the
* lowest descender in the text.
*/
public static final int ALIGN_BOTTOM = 0;
-
+
/**
* A constant indicating that the bottom of this span should be aligned
* with the baseline of the surrounding text.
*/
public static final int ALIGN_BASELINE = 1;
-
+
protected final int mVerticalAlignment;
-
+
+ private WeakReference<Drawable> mDrawableRef;
+
+ /**
+ * Creates a {@link DynamicDrawableSpan}. The default vertical alignment is
+ * {@link #ALIGN_BOTTOM}
+ */
public DynamicDrawableSpan() {
mVerticalAlignment = ALIGN_BOTTOM;
}
/**
- * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}.
+ * Creates a {@link DynamicDrawableSpan} based on a vertical alignment.\
+ *
+ * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}
*/
protected DynamicDrawableSpan(int verticalAlignment) {
mVerticalAlignment = verticalAlignment;
@@ -64,22 +106,22 @@
}
/**
- * Your subclass must implement this method to provide the bitmap
+ * Your subclass must implement this method to provide the bitmap
* to be drawn. The dimensions of the bitmap must be the same
* from each call to the next.
*/
public abstract Drawable getDrawable();
@Override
- public int getSize(Paint paint, CharSequence text,
- int start, int end,
- Paint.FontMetricsInt fm) {
+ public int getSize(@NonNull Paint paint, CharSequence text,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end,
+ @Nullable Paint.FontMetricsInt fm) {
Drawable d = getCachedDrawable();
Rect rect = d.getBounds();
if (fm != null) {
- fm.ascent = -rect.bottom;
- fm.descent = 0;
+ fm.ascent = -rect.bottom;
+ fm.descent = 0;
fm.top = fm.ascent;
fm.bottom = 0;
@@ -89,12 +131,12 @@
}
@Override
- public void draw(Canvas canvas, CharSequence text,
- int start, int end, float x,
- int top, int y, int bottom, Paint paint) {
+ public void draw(@NonNull Canvas canvas, CharSequence text,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x,
+ int top, int y, int bottom, @NonNull Paint paint) {
Drawable b = getCachedDrawable();
canvas.save();
-
+
int transY = bottom - b.getBounds().bottom;
if (mVerticalAlignment == ALIGN_BASELINE) {
transY -= paint.getFontMetricsInt().descent;
@@ -109,8 +151,9 @@
WeakReference<Drawable> wr = mDrawableRef;
Drawable d = null;
- if (wr != null)
+ if (wr != null) {
d = wr.get();
+ }
if (d == null) {
d = getDrawable();
@@ -119,7 +162,5 @@
return d;
}
-
- private WeakReference<Drawable> mDrawableRef;
}
diff --git a/core/java/android/text/style/IconMarginSpan.java b/core/java/android/text/style/IconMarginSpan.java
index 304c83f..ad78bd5 100644
--- a/core/java/android/text/style/IconMarginSpan.java
+++ b/core/java/android/text/style/IconMarginSpan.java
@@ -16,57 +16,98 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Px;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Layout;
import android.text.Spanned;
-public class IconMarginSpan
-implements LeadingMarginSpan, LineHeightSpan
-{
- public IconMarginSpan(Bitmap b) {
- mBitmap = b;
+/**
+ * Paragraph affecting span, that draws a bitmap at the beginning of a text. The span also allows
+ * setting a padding between the bitmap and the text. The default value of the padding is 0px. The
+ * span should be attached from the first character of the text.
+ * <p>
+ * For example, an <code>IconMarginSpan</code> with a bitmap and a padding of 30px can be set
+ * like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with icon and padding");
+ * string.setSpan(new IconMarginSpan(bitmap, 30), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/iconmarginspan.png" />
+ * <figcaption>Text with <code>IconMarginSpan</code></figcaption>
+ * <p>
+ *
+ * @see DrawableMarginSpan for working with a {@link android.graphics.drawable.Drawable} instead of
+ * a {@link Bitmap}.
+ */
+public class IconMarginSpan implements LeadingMarginSpan, LineHeightSpan {
+
+ @NonNull
+ private final Bitmap mBitmap;
+ @Px
+ private final int mPad;
+
+ /**
+ * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
+ *
+ * @param bitmap bitmap to be rendered at the beginning of the text
+ */
+ public IconMarginSpan(@NonNull Bitmap bitmap) {
+ this(bitmap, 0);
}
- public IconMarginSpan(Bitmap b, int pad) {
- mBitmap = b;
+ /**
+ * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
+ *
+ * @param bitmap bitmap to be rendered at the beginning of the text
+ * @param pad padding width, in pixels, between the bitmap and the text
+ */
+ public IconMarginSpan(@NonNull Bitmap bitmap, @IntRange(from = 0) int pad) {
+ mBitmap = bitmap;
mPad = pad;
}
+ @Override
public int getLeadingMargin(boolean first) {
return mBitmap.getWidth() + mPad;
}
+ @Override
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
- int top, int baseline, int bottom,
- CharSequence text, int start, int end,
- boolean first, Layout layout) {
+ int top, int baseline, int bottom,
+ CharSequence text, int start, int end,
+ boolean first, Layout layout) {
int st = ((Spanned) text).getSpanStart(this);
int itop = layout.getLineTop(layout.getLineForOffset(st));
- if (dir < 0)
+ if (dir < 0) {
x -= mBitmap.getWidth();
+ }
c.drawBitmap(mBitmap, x, itop, p);
}
+ @Override
public void chooseHeight(CharSequence text, int start, int end,
- int istartv, int v,
- Paint.FontMetricsInt fm) {
+ int istartv, int v,
+ Paint.FontMetricsInt fm) {
if (end == ((Spanned) text).getSpanEnd(this)) {
int ht = mBitmap.getHeight();
int need = ht - (v + fm.descent - fm.ascent - istartv);
- if (need > 0)
+ if (need > 0) {
fm.descent += need;
+ }
need = ht - (v + fm.bottom - fm.top - istartv);
- if (need > 0)
+ if (need > 0) {
fm.bottom += need;
+ }
}
}
- private Bitmap mBitmap;
- private int mPad;
}
diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java
index b0bff68..95f0b43 100644
--- a/core/java/android/text/style/ImageSpan.java
+++ b/core/java/android/text/style/ImageSpan.java
@@ -17,6 +17,8 @@
package android.text.style;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -27,18 +29,49 @@
import java.io.InputStream;
+/**
+ * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with
+ * the bottom or with the baseline of the surrounding text. The drawable can be constructed from
+ * varied sources:
+ * <ul>
+ * <li>{@link Bitmap} - see {@link #ImageSpan(Context, Bitmap)} and
+ * {@link #ImageSpan(Context, Bitmap, int)}
+ * </li>
+ * <li>{@link Drawable} - see {@link #ImageSpan(Drawable, int)}</li>
+ * <li>resource id - see {@link #ImageSpan(Context, int, int)}</li>
+ * <li>{@link Uri} - see {@link #ImageSpan(Context, Uri, int)}</li>
+ * </ul>
+ * The default value for the vertical alignment is {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ * <p>
+ * For example, an <code>ImagedSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = SpannableString("Bottom: span.\nBaseline: span.");
+ * // using the default alignment: ALIGN_BOTTOM
+ * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher), 7, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher, DynamicDrawableSpan.ALIGN_BASELINE),
+ * 22, 23, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/imagespan.png" />
+ * <figcaption>Text with <code>ImageSpan</code>s aligned bottom and baseline.</figcaption>
+ */
public class ImageSpan extends DynamicDrawableSpan {
+
+ @Nullable
private Drawable mDrawable;
+ @Nullable
private Uri mContentUri;
+ @DrawableRes
private int mResourceId;
+ @Nullable
private Context mContext;
+ @Nullable
private String mSource;
/**
* @deprecated Use {@link #ImageSpan(Context, Bitmap)} instead.
*/
@Deprecated
- public ImageSpan(Bitmap b) {
+ public ImageSpan(@NonNull Bitmap b) {
this(null, b, ALIGN_BOTTOM);
}
@@ -46,80 +79,143 @@
* @deprecated Use {@link #ImageSpan(Context, Bitmap, int)} instead.
*/
@Deprecated
- public ImageSpan(Bitmap b, int verticalAlignment) {
+ public ImageSpan(@NonNull Bitmap b, int verticalAlignment) {
this(null, b, verticalAlignment);
}
- public ImageSpan(Context context, Bitmap b) {
- this(context, b, ALIGN_BOTTOM);
+ /**
+ * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Bitmap} with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on the display
+ * metrics of the resources
+ * @param bitmap bitmap to be rendered
+ */
+ public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap) {
+ this(context, bitmap, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Bitmap} and a vertical
+ * alignment.
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on
+ * the display metrics of the resources
+ * @param bitmap bitmap to be rendered
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Context context, Bitmap b, int verticalAlignment) {
+ public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap, int verticalAlignment) {
super(verticalAlignment);
mContext = context;
mDrawable = context != null
- ? new BitmapDrawable(context.getResources(), b)
- : new BitmapDrawable(b);
+ ? new BitmapDrawable(context.getResources(), bitmap)
+ : new BitmapDrawable(bitmap);
int width = mDrawable.getIntrinsicWidth();
int height = mDrawable.getIntrinsicHeight();
- mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0);
- }
-
- public ImageSpan(Drawable d) {
- this(d, ALIGN_BOTTOM);
+ mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0);
}
/**
- * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * Constructs an {@link ImageSpan} from a drawable with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}.
+ *
+ * @param drawable drawable to be rendered
*/
- public ImageSpan(Drawable d, int verticalAlignment) {
- super(verticalAlignment);
- mDrawable = d;
- }
-
- public ImageSpan(Drawable d, String source) {
- this(d, source, ALIGN_BOTTOM);
+ public ImageSpan(@NonNull Drawable drawable) {
+ this(drawable, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a drawable and a vertical alignment.
+ *
+ * @param drawable drawable to be rendered
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Drawable d, String source, int verticalAlignment) {
+ public ImageSpan(@NonNull Drawable drawable, int verticalAlignment) {
super(verticalAlignment);
- mDrawable = d;
+ mDrawable = drawable;
+ }
+
+ /**
+ * Constructs an {@link ImageSpan} from a drawable and a source with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ *
+ * @param drawable drawable to be rendered
+ * @param source drawable's Uri source
+ */
+ public ImageSpan(@NonNull Drawable drawable, @NonNull String source) {
+ this(drawable, source, ALIGN_BOTTOM);
+ }
+
+ /**
+ * Constructs an {@link ImageSpan} from a drawable, a source and a vertical alignment.
+ *
+ * @param drawable drawable to be rendered
+ * @param source drawable's uri source
+ * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
+ */
+ public ImageSpan(@NonNull Drawable drawable, @NonNull String source, int verticalAlignment) {
+ super(verticalAlignment);
+ mDrawable = drawable;
mSource = source;
}
- public ImageSpan(Context context, Uri uri) {
+ /**
+ * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Uri} with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}. The Uri source can be retrieved via
+ * {@link #getSource()}
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on the display
+ * metrics of the resources
+ * @param uri {@link Uri} used to construct the drawable that will be rendered
+ */
+ public ImageSpan(@NonNull Context context, @NonNull Uri uri) {
this(context, uri, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Uri} and a vertical
+ * alignment. The Uri source can be retrieved via {@link #getSource()}
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on
+ * the display
+ * metrics of the resources
+ * @param uri {@link Uri} used to construct the drawable that will be rendered.
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Context context, Uri uri, int verticalAlignment) {
+ public ImageSpan(@NonNull Context context, @NonNull Uri uri, int verticalAlignment) {
super(verticalAlignment);
mContext = context;
mContentUri = uri;
mSource = uri.toString();
}
- public ImageSpan(Context context, @DrawableRes int resourceId) {
+ /**
+ * Constructs an {@link ImageSpan} from a {@link Context} and a resource id with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ *
+ * @param context context used to retrieve the drawable from resources
+ * @param resourceId drawable resource id based on which the drawable is retrieved
+ */
+ public ImageSpan(@NonNull Context context, @DrawableRes int resourceId) {
this(context, resourceId, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a {@link Context}, a resource id and a vertical
+ * alignment.
+ *
+ * @param context context used to retrieve the drawable from resources
+ * @param resourceId drawable resource id based on which the drawable is retrieved.
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Context context, @DrawableRes int resourceId, int verticalAlignment) {
+ public ImageSpan(@NonNull Context context, @DrawableRes int resourceId,
+ int verticalAlignment) {
super(verticalAlignment);
mContext = context;
mResourceId = resourceId;
@@ -128,10 +224,10 @@
@Override
public Drawable getDrawable() {
Drawable drawable = null;
-
+
if (mDrawable != null) {
drawable = mDrawable;
- } else if (mContentUri != null) {
+ } else if (mContentUri != null) {
Bitmap bitmap = null;
try {
InputStream is = mContext.getContentResolver().openInputStream(
@@ -142,7 +238,7 @@
drawable.getIntrinsicHeight());
is.close();
} catch (Exception e) {
- Log.e("sms", "Failed to loaded content " + mContentUri, e);
+ Log.e("ImageSpan", "Failed to loaded content " + mContentUri, e);
}
} else {
try {
@@ -150,8 +246,8 @@
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
} catch (Exception e) {
- Log.e("sms", "Unable to find resource: " + mResourceId);
- }
+ Log.e("ImageSpan", "Unable to find resource: " + mResourceId);
+ }
}
return drawable;
@@ -159,9 +255,12 @@
/**
* Returns the source string that was saved during construction.
+ *
+ * @return the source string that was saved during construction
+ * @see #ImageSpan(Drawable, String) and this{@link #ImageSpan(Context, Uri)}
*/
+ @Nullable
public String getSource() {
return mSource;
}
-
}
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 1ebee82..50ee5f3 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -19,16 +19,42 @@
import android.graphics.Paint;
import android.text.TextPaint;
-public interface LineHeightSpan
-extends ParagraphStyle, WrapTogetherSpan
-{
+/**
+ * The classes that affect the height of the line should implement this interface.
+ */
+public interface LineHeightSpan extends ParagraphStyle, WrapTogetherSpan {
+ /**
+ * Classes that implement this should define how the height is being calculated.
+ *
+ * @param text the text
+ * @param start the start of the line
+ * @param end the end of the line
+ * @param spanstartv the start of the span
+ * @param lineHeight the line height
+ * @param fm font metrics of the paint, in integers
+ */
public void chooseHeight(CharSequence text, int start, int end,
- int spanstartv, int v,
- Paint.FontMetricsInt fm);
+ int spanstartv, int lineHeight,
+ Paint.FontMetricsInt fm);
+ /**
+ * The classes that affect the height of the line with respect to density, should implement this
+ * interface.
+ */
public interface WithDensity extends LineHeightSpan {
+
+ /**
+ * Classes that implement this should define how the height is being calculated.
+ *
+ * @param text the text
+ * @param start the start of the line
+ * @param end the end of the line
+ * @param spanstartv the start of the span
+ * @param lineHeight the line height
+ * @param paint the paint
+ */
public void chooseHeight(CharSequence text, int start, int end,
- int spanstartv, int v,
- Paint.FontMetricsInt fm, TextPaint paint);
+ int spanstartv, int lineHeight,
+ Paint.FontMetricsInt fm, TextPaint paint);
}
}
diff --git a/core/java/android/text/style/MaskFilterSpan.java b/core/java/android/text/style/MaskFilterSpan.java
index 2ff52a8..d76ef94 100644
--- a/core/java/android/text/style/MaskFilterSpan.java
+++ b/core/java/android/text/style/MaskFilterSpan.java
@@ -18,15 +18,36 @@
import android.graphics.MaskFilter;
import android.text.TextPaint;
-
+/**
+ * Span that allows setting a {@link MaskFilter} to the text it's attached to.
+ * <p>
+ * For example, to blur a text, a {@link android.graphics.BlurMaskFilter} can be used:
+ * <pre>
+ * MaskFilter blurMask = new BlurMaskFilter(5f, BlurMaskFilter.Blur.NORMAL);
+ * SpannableString string = new SpannableString("Text with blur mask");
+ * string.setSpan(new MaskFilterSpan(blurMask), 10, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/maskfilterspan.png" />
+ * <figcaption>Text blurred with the <code>MaskFilterSpan</code>.</figcaption>
+ */
public class MaskFilterSpan extends CharacterStyle implements UpdateAppearance {
private MaskFilter mFilter;
+ /**
+ * Creates a {@link MaskFilterSpan} from a {@link MaskFilter}.
+ *
+ * @param filter the filter to be applied to the <code>TextPaint</code>
+ */
public MaskFilterSpan(MaskFilter filter) {
mFilter = filter;
}
+ /**
+ * Return the mask filter for this span.
+ *
+ * @return the mask filter for this span
+ */
public MaskFilter getMaskFilter() {
return mFilter;
}
diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java
index f900db5..bdfa700 100644
--- a/core/java/android/text/style/StyleSpan.java
+++ b/core/java/android/text/style/StyleSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+import android.annotation.NonNull;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Parcel;
@@ -24,55 +25,76 @@
import android.text.TextUtils;
/**
- *
- * Describes a style in a span.
+ * Span that allows setting the style of the text it's attached to.
+ * Possible styles are: {@link Typeface#NORMAL}, {@link Typeface#BOLD}, {@link Typeface#ITALIC} and
+ * {@link Typeface#BOLD_ITALIC}.
+ * <p>
* Note that styles are cumulative -- if both bold and italic are set in
* separate spans, or if the base style is bold and a span calls for italic,
* you get bold italic. You can't turn off a style from the base style.
- *
+ * <p>
+ * For example, the <code>StyleSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Bold and italic text");
+ * string.setSpan(new StyleSpan(Typeface.BOLD), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(new StyleSpan(Typeface.ITALIC), 9, 15, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/stylespan.png" />
+ * <figcaption>Text styled bold and italic with the <code>StyleSpan</code>.</figcaption>
*/
public class StyleSpan extends MetricAffectingSpan implements ParcelableSpan {
private final int mStyle;
/**
- *
+ * Creates a {@link StyleSpan} from a style.
+ *
* @param style An integer constant describing the style for this span. Examples
- * include bold, italic, and normal. Values are constants defined
- * in {@link android.graphics.Typeface}.
+ * include bold, italic, and normal. Values are constants defined
+ * in {@link Typeface}.
*/
public StyleSpan(int style) {
mStyle = style;
}
- public StyleSpan(Parcel src) {
+ /**
+ * Creates a {@link StyleSpan} from a parcel.
+ *
+ * @param src the parcel
+ */
+ public StyleSpan(@NonNull Parcel src) {
mStyle = src.readInt();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.STYLE_SPAN;
}
-
+
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeInt(mStyle);
}
/**
- * Returns the style constant defined in {@link android.graphics.Typeface}.
+ * Returns the style constant defined in {@link Typeface}.
*/
public int getStyle() {
return mStyle;
diff --git a/core/java/android/text/style/TabStopSpan.java b/core/java/android/text/style/TabStopSpan.java
index 0566428..2cceb2c 100644
--- a/core/java/android/text/style/TabStopSpan.java
+++ b/core/java/android/text/style/TabStopSpan.java
@@ -16,39 +16,54 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.Px;
+
/**
- * Represents a single tab stop on a line.
+ * Paragraph affecting span that changes the position of the tab with respect to
+ * the leading margin of the line. <code>TabStopSpan</code> will only affect the first tab
+ * encountered on the first line of the text.
*/
-public interface TabStopSpan
-extends ParagraphStyle
-{
- /**
- * Returns the offset of the tab stop from the leading margin of the
- * line.
- * @return the offset
- */
- public int getTabStop();
+public interface TabStopSpan extends ParagraphStyle {
/**
- * The default implementation of TabStopSpan.
+ * Returns the offset of the tab stop from the leading margin of the line, in pixels.
+ *
+ * @return the offset, in pixels
*/
- public static class Standard
- implements TabStopSpan
- {
+ int getTabStop();
+
+ /**
+ * The default implementation of TabStopSpan that allows setting the offset of the tab stop
+ * from the leading margin of the first line of text.
+ * <p>
+ * Let's consider that we have the following text: <i>"\tParagraph text beginning with tab."</i>
+ * and we want to move the tab stop with 100px. This can be achieved like this:
+ * <pre>
+ * SpannableString string = new SpannableString("\tParagraph text beginning with tab.");
+ * string.setSpan(new TabStopSpan.Standard(100), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/tabstopspan.png" />
+ * <figcaption>Text with a tab stop and a <code>TabStopSpan</code></figcaption>
+ */
+ class Standard implements TabStopSpan {
+
+ @Px
+ private int mTabOffset;
+
/**
- * Constructor.
+ * Constructs a {@link TabStopSpan.Standard} based on an offset.
*
- * @param where the offset of the tab stop from the leading margin of
- * the line
+ * @param offset the offset of the tab stop from the leading margin of
+ * the line, in pixels
*/
- public Standard(int where) {
- mTab = where;
+ public Standard(@IntRange(from = 0) int offset) {
+ mTabOffset = offset;
}
+ @Override
public int getTabStop() {
- return mTab;
+ return mTabOffset;
}
-
- private int mTab;
}
}
diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java
index aa622d8..1622812 100644
--- a/core/java/android/text/style/TypefaceSpan.java
+++ b/core/java/android/text/style/TypefaceSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+import android.annotation.NonNull;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Parcel;
@@ -24,42 +25,59 @@
import android.text.TextUtils;
/**
- * Changes the typeface family of the text to which the span is attached.
+ * Changes the typeface family of the text to which the span is attached. Examples of typeface
+ * family include "monospace", "serif", and "sans-serif".
+ * <p>
+ * For example, change the typeface of a text to "monospace" like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with typeface span");
+ * string.setSpan(new TypefaceSpan("monospace"), 10, 18, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/typefacespan.png" />
+ * <figcaption>Text with "monospace" typeface family.</figcaption>
*/
public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan {
+
private final String mFamily;
/**
- * @param family The font family for this typeface. Examples include
- * "monospace", "serif", and "sans-serif".
+ * Constructs a {@link TypefaceSpan} based on a font family.
+ *
+ * @param family The font family for this typeface. Examples include
+ * "monospace", "serif", and "sans-serif".
*/
public TypefaceSpan(String family) {
mFamily = family;
}
- public TypefaceSpan(Parcel src) {
+ public TypefaceSpan(@NonNull Parcel src) {
mFamily = src.readString();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.TYPEFACE_SPAN;
}
-
+
+ @Override
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel dest, int flags) {
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeString(mFamily);
}
@@ -71,16 +89,16 @@
}
@Override
- public void updateDrawState(TextPaint ds) {
- apply(ds, mFamily);
+ public void updateDrawState(@NonNull TextPaint textPaint) {
+ apply(textPaint, mFamily);
}
@Override
- public void updateMeasureState(TextPaint paint) {
- apply(paint, mFamily);
+ public void updateMeasureState(@NonNull TextPaint textPaint) {
+ apply(textPaint, mFamily);
}
- private static void apply(Paint paint, String family) {
+ private static void apply(@NonNull Paint paint, String family) {
int oldStyle;
Typeface old = paint.getTypeface();
diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java
index 58239ef..eab1ef4 100644
--- a/core/java/android/text/style/URLSpan.java
+++ b/core/java/android/text/style/URLSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+import android.annotation.NonNull;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -27,40 +28,71 @@
import android.util.Log;
import android.view.View;
+/**
+ * Implementation of the {@link ClickableSpan} that allows setting a url string. When
+ * selecting and clicking on the text to which the span is attached, the <code>URLSpan</code>
+ * will try to open the url, by launching an an Activity with an {@link Intent#ACTION_VIEW} intent.
+ * <p>
+ * For example, a <code>URLSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with a url span");
+ * string.setSpan(new URLSpan("http://www.developer.android.com"), 12, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/urlspan.png" />
+ * <figcaption>Text with <code>URLSpan</code>.</figcaption>
+ */
public class URLSpan extends ClickableSpan implements ParcelableSpan {
private final String mURL;
+ /**
+ * Constructs a {@link URLSpan} from a url string.
+ *
+ * @param url the url string
+ */
public URLSpan(String url) {
mURL = url;
}
- public URLSpan(Parcel src) {
+ /**
+ * Constructs a {@link URLSpan} from a parcel.
+ */
+ public URLSpan(@NonNull Parcel src) {
mURL = src.readString();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.URL_SPAN;
}
-
+
+ @Override
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel dest, int flags) {
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeString(mURL);
}
+ /**
+ * Get the url string for this span.
+ *
+ * @return the url string.
+ */
public String getURL() {
return mURL;
}
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 3fd4696..8cb46b7 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -23,6 +23,10 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@@ -396,6 +400,33 @@
return true;
}
+ /**
+ * Get the Bitmap from the Drawable.
+ *
+ * If the Bitmap needed to be scaled up to account for density, BitmapDrawable
+ * handles this at draw time. But this class doesn't actually draw the Bitmap;
+ * it is just a holder for native code to access its SkBitmap. So this needs to
+ * get a version that is scaled to account for density.
+ */
+ private Bitmap getBitmapFromDrawable(BitmapDrawable bitmapDrawable) {
+ Bitmap bitmap = bitmapDrawable.getBitmap();
+ final int scaledWidth = bitmapDrawable.getIntrinsicWidth();
+ final int scaledHeight = bitmapDrawable.getIntrinsicHeight();
+ if (scaledWidth == bitmap.getWidth() && scaledHeight == bitmap.getHeight()) {
+ return bitmap;
+ }
+
+ Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ RectF dst = new RectF(0, 0, scaledWidth, scaledHeight);
+
+ Bitmap scaled = Bitmap.createBitmap(scaledWidth, scaledHeight, bitmap.getConfig());
+ Canvas canvas = new Canvas(scaled);
+ Paint paint = new Paint();
+ paint.setFilterBitmap(true);
+ canvas.drawBitmap(bitmap, src, dst, paint);
+ return scaled;
+ }
+
private void loadResource(Context context, Resources resources, @XmlRes int resourceId) {
final XmlResourceParser parser = resources.getXml(resourceId);
final int bitmapRes;
@@ -452,7 +483,8 @@
+ "is different. All frames should have the exact same size and "
+ "share the same hotspot.");
}
- mBitmapFrames[i - 1] = ((BitmapDrawable)drawableFrame).getBitmap();
+ BitmapDrawable bitmapDrawableFrame = (BitmapDrawable) drawableFrame;
+ mBitmapFrames[i - 1] = getBitmapFromDrawable(bitmapDrawableFrame);
}
}
}
@@ -461,7 +493,8 @@
+ "refer to a bitmap drawable.");
}
- final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+ final Bitmap bitmap = getBitmapFromDrawable(bitmapDrawable);
validateHotSpot(bitmap, hotSpotX, hotSpotY);
// Set the properties now that we have successfully loaded the icon.
mBitmap = bitmap;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3cbd275..c3e9e73 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -23689,9 +23689,16 @@
Point shadowTouchPoint = new Point();
shadowBuilder.onProvideShadowMetrics(shadowSize, shadowTouchPoint);
- if ((shadowSize.x <= 0) || (shadowSize.y <= 0)
+ if ((shadowSize.x < 0) || (shadowSize.y < 0)
|| (shadowTouchPoint.x < 0) || (shadowTouchPoint.y < 0)) {
- throw new IllegalStateException("Drag shadow dimensions must be positive");
+ throw new IllegalStateException("Drag shadow dimensions must not be negative");
+ }
+
+ // Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder
+ // does not accept zero size surface.
+ if (shadowSize.x == 0 || shadowSize.y == 0) {
+ shadowSize.x = 1;
+ shadowSize.y = 1;
}
if (ViewDebug.DEBUG_DRAG) {
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 2e4cccf..e58e62f 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -19,12 +19,11 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.Context;
import android.media.session.MediaController;
import android.media.update.ApiLoader;
-import android.media.update.FrameLayoutHelper;
import android.media.update.MediaControlView2Provider;
+import android.media.update.ViewGroupHelper;
import android.util.AttributeSet;
import java.lang.annotation.Retention;
@@ -62,7 +61,7 @@
* TODO PUBLIC API
* @hide
*/
-public class MediaControlView2 extends FrameLayoutHelper<MediaControlView2Provider> {
+public class MediaControlView2 extends ViewGroupHelper<MediaControlView2Provider> {
/** @hide */
@IntDef({
BUTTON_PLAY_PAUSE,
@@ -156,18 +155,12 @@
public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
- super((instance, superProvider) ->
+ super((instance, superProvider, privateProvider) ->
ApiLoader.getProvider(context).createMediaControlView2(
- (MediaControlView2) instance, superProvider),
+ (MediaControlView2) instance, superProvider, privateProvider,
+ attrs, defStyleAttr, defStyleRes),
context, attrs, defStyleAttr, defStyleRes);
- }
-
- /**
- * @hide
- */
- @SystemApi
- public MediaControlView2Provider getProvider() {
- return mProvider;
+ mProvider.initialize(attrs, defStyleAttr, defStyleRes);
}
/**
@@ -178,13 +171,6 @@
}
/**
- * Returns whether the control view is currently shown or hidden.
- */
- public boolean isShowing() {
- return mProvider.isShowing_impl();
- }
-
- /**
* Changes the visibility state of an individual button. Default value is View.Visible.
*
* @param button the {@code Button} assigned to individual buttons
@@ -232,9 +218,7 @@
}
@Override
- // TODO Move this method to ViewProvider
- public void onVisibilityAggregated(boolean isVisible) {
-
- mProvider.onVisibilityAggregated_impl(isVisible);
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ mProvider.onLayout_impl(changed, l, t, r, b);
}
}
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 78ca011..3081180 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -27,8 +27,8 @@
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.media.update.ApiLoader;
-import android.media.update.FrameLayoutHelper;
import android.media.update.VideoView2Provider;
+import android.media.update.ViewGroupHelper;
import android.net.Uri;
import android.os.Bundle;
import android.util.AttributeSet;
@@ -101,7 +101,7 @@
*
* @hide
*/
-public class VideoView2 extends FrameLayoutHelper<VideoView2Provider> {
+public class VideoView2 extends ViewGroupHelper<VideoView2Provider> {
/** @hide */
@IntDef({
VIEW_TYPE_TEXTUREVIEW,
@@ -139,10 +139,12 @@
public VideoView2(
@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
- super((instance, superProvider) ->
+ super((instance, superProvider, privateProvider) ->
ApiLoader.getProvider(context).createVideoView2(
- (VideoView2) instance, superProvider, attrs, defStyleAttr, defStyleRes),
+ (VideoView2) instance, superProvider, privateProvider,
+ attrs, defStyleAttr, defStyleRes),
context, attrs, defStyleAttr, defStyleRes);
+ mProvider.initialize(attrs, defStyleAttr, defStyleRes);
}
/**
@@ -487,4 +489,9 @@
*/
void onCustomAction(String action, Bundle extras);
}
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ mProvider.onLayout_impl(changed, l, t, r, b);
+ }
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 7def876..40dcf25b 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -3880,7 +3880,8 @@
public void addIsolatedUidLocked(int isolatedUid, int appUid) {
mIsolatedUids.put(isolatedUid, appUid);
- StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid, 1);
+ StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid,
+ StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
final Uid u = getUidStatsLocked(appUid);
u.addIsolatedUid(isolatedUid);
}
@@ -3904,7 +3905,8 @@
*/
public void removeIsolatedUidLocked(int isolatedUid) {
StatsLog.write(
- StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1), isolatedUid, 0);
+ StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1),
+ isolatedUid, StatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
final int idx = mIsolatedUids.indexOfKey(isolatedUid);
if (idx >= 0) {
final int ownerUid = mIsolatedUids.valueAt(idx);
@@ -4255,10 +4257,12 @@
if (wc != null) {
StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
- getPowerManagerWakeLockLevel(type), name, 1);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
} else {
StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
- getPowerManagerWakeLockLevel(type), name, 1);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
}
}
}
@@ -4298,10 +4302,12 @@
getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime);
if (wc != null) {
StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
- getPowerManagerWakeLockLevel(type), name, 0);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
} else {
StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
- getPowerManagerWakeLockLevel(type), name, 0);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
}
}
}
@@ -4426,7 +4432,8 @@
public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uid, null, name, historyName, 1);
+ uid, null, name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
uid = mapUid(uid);
noteLongPartialWakeLockStartInternal(name, historyName, uid);
@@ -4439,7 +4446,8 @@
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockStartInternal(name, historyName, uid);
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workSource.get(i), workSource.getName(i), name, historyName, 1);
+ workSource.get(i), workSource.getName(i), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4450,7 +4458,8 @@
noteLongPartialWakeLockStartInternal(name, historyName, uid);
StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), name, historyName, 1);
+ workChain.getUids(), workChain.getTags(), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
}
}
}
@@ -4470,8 +4479,8 @@
}
public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
- StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uid, null, name, historyName, 0);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, null,
+ name, historyName, StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
uid = mapUid(uid);
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
@@ -4484,7 +4493,8 @@
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workSource.get(i), workSource.getName(i), name, historyName, 0);
+ workSource.get(i), workSource.getName(i), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4494,7 +4504,8 @@
final int uid = workChain.getAttributionUid();
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), name, historyName, 0);
+ workChain.getUids(), workChain.getTags(), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -4518,7 +4529,8 @@
long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
timer.add(deltaUptime * 1000, 1); // time in in microseconds
- StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason, deltaUptime * 1000);
+ StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason,
+ /* duration_usec */ deltaUptime * 1000);
mLastWakeupReason = null;
}
}
@@ -4659,10 +4671,12 @@
mGpsNesting++;
if (workChain == null) {
- StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, 1);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
} else {
StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
}
getUidStatsLocked(uid).noteStartGps(elapsedRealtime);
@@ -4683,10 +4697,11 @@
}
if (workChain == null) {
- StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, 0);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
} else {
StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(),
- workChain.getTags(), 0);
+ workChain.getTags(), StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
}
getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
@@ -4941,7 +4956,9 @@
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtime);
}
addHistoryRecordLocked(elapsedRealtime, uptime);
- StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ? 1 : 0);
+ StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ?
+ StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON :
+ StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
}
}
@@ -5545,16 +5562,19 @@
if (workChain != null) {
StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON);
if (isUnoptimized) {
StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON);
}
} else {
- StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 1);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON);
if (isUnoptimized) {
StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
- 1);
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON);
}
}
@@ -5594,16 +5614,19 @@
if (workChain != null) {
StatsLog.write(
- StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(), 0);
+ StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
if (isUnoptimized) {
StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
}
} else {
- StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 0);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
if (isUnoptimized) {
StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
- 0);
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
}
}
@@ -5656,7 +5679,8 @@
for (int j = 0; j < allWorkChains.size(); ++j) {
StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
allWorkChains.get(j).getUids(),
- allWorkChains.get(j).getTags(), 0);
+ allWorkChains.get(j).getTags(),
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
}
allWorkChains.clear();
}
@@ -5666,7 +5690,8 @@
for (int j = 0; j < unoptimizedWorkChains.size(); ++j) {
StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
unoptimizedWorkChains.get(j).getUids(),
- unoptimizedWorkChains.get(j).getTags(), 0);
+ unoptimizedWorkChains.get(j).getTags(),
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
}
unoptimizedWorkChains.clear();
}
@@ -6011,7 +6036,8 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockAcquiredLocked(uid);
- StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 1);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6021,7 +6047,8 @@
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockAcquiredLocked(uid);
StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON);
}
}
}
@@ -6031,7 +6058,8 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockReleasedLocked(uid);
- StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 0);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6041,7 +6069,8 @@
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockReleasedLocked(uid);
StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -6052,7 +6081,7 @@
final int uid = mapUid(ws.get(i));
noteWifiScanStartedLocked(uid);
StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
- 1);
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6062,7 +6091,7 @@
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStartedLocked(uid);
StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, workChain.getUids(),
- workChain.getTags(), 1);
+ workChain.getTags(), StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
}
}
}
@@ -6073,7 +6102,7 @@
final int uid = mapUid(ws.get(i));
noteWifiScanStoppedLocked(uid);
StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
- 0);
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6083,7 +6112,8 @@
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStoppedLocked(uid);
StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7035,7 +7065,8 @@
}
mWifiMulticastTimer.startRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 1);
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__ON);
}
}
@@ -7045,7 +7076,8 @@
mWifiMulticastEnabled = false;
mWifiMulticastTimer.stopRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__OFF);
}
}
@@ -7098,14 +7130,16 @@
public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 1);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+ StatsLog.AUDIO_STATE_CHANGED__STATE__ON);
}
public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+ StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7113,7 +7147,8 @@
public void noteResetAudioLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+ StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
}
}
@@ -7127,7 +7162,8 @@
public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null, 1);
+ StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
+ StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__ON);
}
public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
@@ -7135,7 +7171,7 @@
mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(),
- null, 0);
+ null, StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
}
}
}
@@ -7144,7 +7180,7 @@
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
- 0);
+ StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
}
}
@@ -7158,7 +7194,8 @@
public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,1);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
+ StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__ON);
}
public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
@@ -7166,7 +7203,7 @@
mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mFlashlightTurnedOnTimer.isRunningLocked()) {
StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
- 0);
+ StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7174,7 +7211,8 @@
public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
+ StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
}
}
@@ -7188,14 +7226,16 @@
public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 1);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+ StatsLog.CAMERA_STATE_CHANGED__STATE__ON);
}
public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+ StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7203,7 +7243,8 @@
public void noteResetCameraLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+ StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
}
}
@@ -9777,7 +9818,8 @@
DualTimer t = mSyncStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 1);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
+ StatsLog.SYNC_STATE_CHANGED__STATE__ON);
}
}
@@ -9786,7 +9828,8 @@
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 0);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
+ StatsLog.SYNC_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -9796,7 +9839,7 @@
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
- name, 1);
+ name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED);
}
}
@@ -9806,7 +9849,7 @@
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
- name, 0);
+ name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED);
}
}
if (mBsi.mOnBatteryTimeBase.isRunning()) {
@@ -9919,7 +9962,7 @@
t.startRunningLocked(elapsedRealtimeMs);
if (sensor != Sensor.GPS) {
StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null, sensor,
- 1);
+ StatsLog.SENSOR_STATE_CHANGED__STATE__ON);
}
}
@@ -9930,7 +9973,7 @@
t.stopRunningLocked(elapsedRealtimeMs);
if (sensor != Sensor.GPS) {
StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null,
- sensor, 0);
+ sensor, StatsLog.SENSOR_STATE_CHANGED__STATE__OFF);
}
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index f9a2341..e69a360 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -55,6 +55,8 @@
public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
/** Do not enfore hidden API access restrictions. */
public static final int DISABLE_HIDDEN_API_CHECKS = 1 << 11;
+ /** Force generation of native debugging information for backtraces. */
+ public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12;
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 39279b5..74802c8 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -583,7 +583,7 @@
installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
uuid, classLoaderContext, seInfo, false /* downgrade */,
- targetSdkVersion, /*profileName*/ null);
+ targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null);
} catch (RemoteException | ServiceSpecificException e) {
// Ignore (but log), we need this on the classpath for fallback mode.
Log.w(TAG, "Failed compiling classpath element for system server: "
diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java
index 71550be..f8885a2 100644
--- a/core/java/com/android/internal/util/FileRotator.java
+++ b/core/java/com/android/internal/util/FileRotator.java
@@ -160,7 +160,7 @@
final File file = new File(mBasePath, name);
final FileInputStream is = new FileInputStream(file);
try {
- Streams.copy(is, zos);
+ FileUtils.copy(is, zos);
} finally {
IoUtils.closeQuietly(is);
}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 5673814..732534c 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -66,6 +66,8 @@
void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList);
KeyChainSnapshot getKeyChainSnapshot();
byte[] generateAndStoreKey(String alias);
+ String generateKey(String alias, in byte[] account);
+ String getKey(String alias);
void removeKey(String alias);
void setSnapshotCreatedPendingIntent(in PendingIntent intent);
Map getRecoverySnapshotVersions();
diff --git a/core/java/com/android/server/backup/SliceBackupHelper.java b/core/java/com/android/server/backup/SliceBackupHelper.java
new file mode 100644
index 0000000..8e5a5ee
--- /dev/null
+++ b/core/java/com/android/server/backup/SliceBackupHelper.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import android.app.backup.BlobBackupHelper;
+import android.app.slice.ISliceManager;
+import android.content.Context;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+public class SliceBackupHelper extends BlobBackupHelper {
+ static final String TAG = "SliceBackupHelper";
+ static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ // Current version of the blob schema
+ static final int BLOB_VERSION = 1;
+
+ // Key under which the payload blob is stored
+ static final String KEY_SLICES = "slices";
+
+ public SliceBackupHelper(Context context) {
+ super(BLOB_VERSION, KEY_SLICES);
+ // context is currently unused
+ }
+
+ @Override
+ protected byte[] getBackupPayload(String key) {
+ byte[] newPayload = null;
+ if (KEY_SLICES.equals(key)) {
+ try {
+ ISliceManager sm = ISliceManager.Stub.asInterface(
+ ServiceManager.getService(Context.SLICE_SERVICE));
+ // TODO: http://b/22388012
+ newPayload = sm.getBackupPayload(UserHandle.USER_SYSTEM);
+ } catch (Exception e) {
+ // Treat as no data
+ Slog.e(TAG, "Couldn't communicate with slice manager");
+ newPayload = null;
+ }
+ }
+ return newPayload;
+ }
+
+ @Override
+ protected void applyRestoredPayload(String key, byte[] payload) {
+ if (DEBUG) Slog.v(TAG, "Got restore of " + key);
+
+ if (KEY_SLICES.equals(key)) {
+ try {
+ ISliceManager sm = ISliceManager.Stub.asInterface(
+ ServiceManager.getService(Context.SLICE_SERVICE));
+ // TODO: http://b/22388012
+ sm.applyRestore(payload, UserHandle.USER_SYSTEM);
+ } catch (Exception e) {
+ Slog.e(TAG, "Couldn't communicate with slice manager");
+ }
+ }
+ }
+
+}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index a96b5dd..47e7a0e7 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -51,6 +51,7 @@
private static final String USAGE_STATS_HELPER = "usage_stats";
private static final String SHORTCUT_MANAGER_HELPER = "shortcut_manager";
private static final String ACCOUNT_MANAGER_HELPER = "account_manager";
+ private static final String SLICES_HELPER = "slices";
// These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME
// are also used in the full-backup file format, so must not change unless steps are
@@ -88,6 +89,7 @@
addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());
+ addHelper(SLICES_HELPER, new SliceBackupHelper(this));
super.onBackup(oldState, data, newState);
}
@@ -116,6 +118,7 @@
addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());
+ addHelper(SLICES_HELPER, new SliceBackupHelper(this));
super.onRestore(data, appVersionCode, newState);
}
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index c88cf5c..ba56d59 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -26,10 +26,11 @@
#include <SkPictureRecorder.h>
#include <hwui/AnimatedImageDrawable.h>
#include <hwui/Canvas.h>
+#include <utils/Looper.h>
using namespace android;
-static jmethodID gAnimatedImageDrawable_postOnAnimationEndMethodID;
+static jmethodID gAnimatedImageDrawable_onAnimationEndMethodID;
// Note: jpostProcess holds a handle to the ImageDecoder.
static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
@@ -123,9 +124,9 @@
return drawable->start();
}
-static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
- drawable->stop();
+ return drawable->stop();
}
// Java's LOOP_INFINITE relies on this being the same.
@@ -137,33 +138,63 @@
drawable->setRepetitionCount(loopCount);
}
-class JniAnimationEndListener : public OnAnimationEndListener {
+class InvokeListener : public MessageHandler {
public:
- JniAnimationEndListener(JNIEnv* env, jobject javaObject) {
+ InvokeListener(JNIEnv* env, jobject javaObject) {
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK);
- mJavaObject = env->NewGlobalRef(javaObject);
+ // Hold a weak reference to break a cycle that would prevent GC.
+ mWeakRef = env->NewWeakGlobalRef(javaObject);
}
- ~JniAnimationEndListener() override {
+ ~InvokeListener() override {
auto* env = get_env_or_die(mJvm);
- env->DeleteGlobalRef(mJavaObject);
+ env->DeleteWeakGlobalRef(mWeakRef);
}
- void onAnimationEnd() override {
+ virtual void handleMessage(const Message&) override {
auto* env = get_env_or_die(mJvm);
- env->CallVoidMethod(mJavaObject, gAnimatedImageDrawable_postOnAnimationEndMethodID);
+ jobject localRef = env->NewLocalRef(mWeakRef);
+ if (localRef) {
+ env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID);
+ }
}
private:
JavaVM* mJvm;
- jobject mJavaObject;
+ jweak mWeakRef;
+};
+
+class JniAnimationEndListener : public OnAnimationEndListener {
+public:
+ JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) {
+ mListener = new InvokeListener(env, javaObject);
+ mLooper = std::move(looper);
+ }
+
+ void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); }
+
+private:
+ sp<InvokeListener> mListener;
+ sp<Looper> mLooper;
};
static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/,
jlong nativePtr, jobject jdrawable) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
- drawable->setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener>(
- new JniAnimationEndListener(env, jdrawable)));
+ if (!jdrawable) {
+ drawable->setOnAnimationEndListener(nullptr);
+ } else {
+ sp<Looper> looper = Looper::getForThread();
+ if (!looper.get()) {
+ doThrowISE(env,
+ "Must set AnimatedImageDrawable's AnimationCallback on a thread with a "
+ "looper!");
+ return;
+ }
+
+ drawable->setOnAnimationEndListener(
+ std::make_unique<JniAnimationEndListener>(std::move(looper), env, jdrawable));
+ }
}
static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
@@ -186,7 +217,7 @@
{ "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter },
{ "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning },
{ "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart },
- { "nStop", "(J)V", (void*) AnimatedImageDrawable_nStop },
+ { "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop },
{ "nSetLoopCount", "(JI)V", (void*) AnimatedImageDrawable_nSetLoopCount },
{ "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
{ "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize },
@@ -195,7 +226,7 @@
int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable");
- gAnimatedImageDrawable_postOnAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "postOnAnimationEnd", "()V");
+ gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V");
return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index dd3e6f0..937b3ff 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -44,11 +44,9 @@
struct NativeFamilyBuilder {
NativeFamilyBuilder(uint32_t langId, int variant)
- : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)),
- allowUnsupportedFont(false) {}
+ : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)) {}
uint32_t langId;
minikin::FontFamily::Variant variant;
- bool allowUnsupportedFont;
std::vector<minikin::Font> fonts;
std::vector<minikin::FontVariation> axes;
};
@@ -70,22 +68,17 @@
}
std::unique_ptr<NativeFamilyBuilder> builder(
reinterpret_cast<NativeFamilyBuilder*>(builderPtr));
+ if (builder->fonts.empty()) {
+ return 0;
+ }
std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
builder->langId, builder->variant, std::move(builder->fonts));
- if (family->getCoverage().length() == 0 && !builder->allowUnsupportedFont) {
+ if (family->getCoverage().length() == 0) {
return 0;
}
return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
}
-static void FontFamily_allowUnsupportedFont(jlong builderPtr) {
- if (builderPtr == 0) {
- return;
- }
- NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
- builder->allowUnsupportedFont = true;
-}
-
static void FontFamily_abort(jlong builderPtr) {
NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
delete builder;
@@ -270,7 +263,6 @@
static const JNINativeMethod gFontFamilyMethods[] = {
{ "nInitBuilder", "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder },
{ "nCreateFamily", "(J)J", (void*)FontFamily_create },
- { "nAllowUnsupportedFont", "(J)V", (void*)FontFamily_allowUnsupportedFont },
{ "nAbort", "(J)V", (void*)FontFamily_abort },
{ "nUnrefFamily", "(J)V", (void*)FontFamily_unref },
{ "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont },
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index c79f5bd..12da273 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -36,6 +36,7 @@
#define ENCODING_AAC_ELD 15
#define ENCODING_AAC_XHE 16
#define ENCODING_AC4 17
+#define ENCODING_E_AC3_JOC 18
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -80,6 +81,8 @@
return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC
case ENCODING_AC4:
return AUDIO_FORMAT_AC4;
+ // case ENCODING_E_AC3_JOC: // FIXME Not defined on the native side yet
+ // return AUDIO_FORMAT_E_AC3_JOC;
case ENCODING_DEFAULT:
return AUDIO_FORMAT_DEFAULT;
default:
@@ -130,6 +133,8 @@
// return ENCODING_AAC_XHE;
case AUDIO_FORMAT_AC4:
return ENCODING_AC4;
+ // case AUDIO_FORMAT_E_AC3_JOC: // FIXME Not defined on the native side yet
+ // return ENCODING_E_AC3_JOC;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 43d5b23..d58b95a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1785,13 +1785,22 @@
<!-- Must be required by an ImsService to ensure that only the
system can bind to it.
- <p>Protection level: signature|privileged
+ <p>Protection level: signature|privileged|vendorPrivileged
@SystemApi
@hide
-->
<permission android:name="android.permission.BIND_IMS_SERVICE"
android:protectionLevel="signature|privileged|vendorPrivileged" />
+ <!-- Must be required by a DataService to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature|privileged|vendorPrivileged
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_DATA_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
<!-- Allows an application to manage embedded subscriptions (those on a eUICC) through
EuiccManager APIs.
<p>Protection level: signature|privileged|development
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3b752c4..ec81df7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1351,6 +1351,10 @@
<string name="fingerprint_error_lockout_permanent">Too many attempts. Fingerprint sensor disabled.</string>
<!-- Generic error message shown when the fingerprint hardware can't recognize the fingerprint -->
<string name="fingerprint_error_unable_to_process">Try again.</string>
+ <!-- Generic error message shown when the user has no enrolled fingerprints -->
+ <string name="fingerprint_error_no_fingerprints">No fingerprints enrolled.</string>
+ <!-- Generic error message shown when the app requests fingerprint authentication on a device without a sensor -->
+ <string name="fingerprint_error_hw_not_present">This device does not have a fingerprint sensor</string>
<!-- Template to be used to name enrolled fingerprints by default. -->
<string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string>
@@ -4806,7 +4810,7 @@
A toast message shown when an app shortcut that was restored from a previous device is clicked,
but it cannot be started because the shortcut was created by a newer version of the app.
-->
- <string name="shortcut_restored_on_lower_version">This shortcut requires latest app</string>
+ <string name="shortcut_restored_on_lower_version">App version downgraded, or isn\u2019t compatible with this shortcut</string>
<!--
A toast message shown when an app shortcut that was restored from a previous device is clicked,
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 22cf1a6..22ddca8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2350,6 +2350,8 @@
<java-symbol type="string" name="fingerprint_error_lockout_permanent" />
<java-symbol type="string" name="fingerprint_name_template" />
<java-symbol type="string" name="fingerprint_not_recognized" />
+ <java-symbol type="string" name="fingerprint_error_no_fingerprints" />
+ <java-symbol type="string" name="fingerprint_error_hw_not_present" />
<!-- Fingerprint config -->
<java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/>
diff --git a/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
new file mode 100644
index 0000000..5989da7
--- /dev/null
+++ b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
@@ -0,0 +1,106 @@
+/*
+ * 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.os;
+
+import static android.os.FileUtils.copyInternalSendfile;
+import static android.os.FileUtils.copyInternalSplice;
+import static android.os.FileUtils.copyInternalUserspace;
+
+import android.os.FileUtils.MemoryPipe;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Param;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+public class FileUtilsBenchmark {
+ @Param({"32", "32000", "32000000"})
+ private int mSize;
+
+ private File mSrc;
+ private File mDest;
+
+ private byte[] mData;
+
+ @BeforeExperiment
+ protected void setUp() throws Exception {
+ mSrc = new File("/data/local/tmp/src");
+ mDest = new File("/data/local/tmp/dest");
+
+ mData = new byte[mSize];
+
+ try (FileOutputStream os = new FileOutputStream(mSrc)) {
+ os.write(mData);
+ }
+ }
+
+ public void timeRegularUserspace(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timeRegularSendfile(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalSendfile(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timePipeSourceUserspace(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (MemoryPipe in = MemoryPipe.createSource(mData);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timePipeSourceSplice(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (MemoryPipe in = MemoryPipe.createSource(mData);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalSplice(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timePipeSinkUserspace(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ MemoryPipe out = MemoryPipe.createSink(mData)) {
+ copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timePipeSinkSplice(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ MemoryPipe out = MemoryPipe.createSink(mData)) {
+ copyInternalSplice(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
index 84409d4..f90ae34 100644
--- a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
+++ b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
@@ -16,8 +16,11 @@
package android.hardware.display;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
@@ -27,20 +30,53 @@
import org.junit.runner.RunWith;
import java.time.LocalDate;
+import java.util.Arrays;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AmbientBrightnessDayStatsTest {
+ private static final LocalDate LOCAL_DATE = LocalDate.now();
+ private static final float[] BUCKET_BOUNDARIES = {0, 1, 10, 100};
+ private static final float[] STATS = {1.3f, 2.6f, 5.8f, 10};
+
+ @Test
+ public void testParamsMustNotBeNull() {
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(null, BUCKET_BOUNDARIES));
+
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(LOCAL_DATE, null));
+
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(null, BUCKET_BOUNDARIES, STATS));
+
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(LOCAL_DATE, null, STATS));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBucketBoundariesMustNotBeEmpty() {
+ new AmbientBrightnessDayStats(LocalDate.now(), new float[]{});
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testStatsAndBoundariesMustHaveSameLength() {
+ float[] stats = Arrays.copyOf(STATS, STATS.length + 1);
+ stats[stats.length - 1] = 0;
+ new AmbientBrightnessDayStats(LOCAL_DATE, BUCKET_BOUNDARIES, stats);
+ }
+
@Test
public void testAmbientBrightnessDayStatsAdd() {
- AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LocalDate.now(),
- new float[]{0, 1, 10, 100});
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES);
dayStats.log(0, 1);
dayStats.log(0.5f, 1.5f);
dayStats.log(50, 12.5f);
dayStats.log(2000, 1.24f);
dayStats.log(-10, 0.5f);
+ assertEquals(4, dayStats.getStats().length);
assertEquals(2.5f, dayStats.getStats()[0], 0);
assertEquals(0, dayStats.getStats()[1], 0);
assertEquals(12.5f, dayStats.getStats()[2], 0);
@@ -48,38 +84,12 @@
}
@Test
- public void testAmbientBrightnessDayStatsEquals() {
- LocalDate today = LocalDate.now();
- AmbientBrightnessDayStats dayStats1 = new AmbientBrightnessDayStats(today,
- new float[]{0, 1, 10, 100});
- AmbientBrightnessDayStats dayStats2 = new AmbientBrightnessDayStats(today,
- new float[]{0, 1, 10, 100}, new float[4]);
- AmbientBrightnessDayStats dayStats3 = new AmbientBrightnessDayStats(today,
- new float[]{0, 1, 10, 100}, new float[]{1, 3, 5, 7});
- AmbientBrightnessDayStats dayStats4 = new AmbientBrightnessDayStats(today,
- new float[]{0, 1, 10, 100}, new float[]{1, 3, 5, 0});
- assertEquals(dayStats1, dayStats2);
- assertEquals(dayStats1.hashCode(), dayStats2.hashCode());
- assertNotEquals(dayStats1, dayStats3);
- assertNotEquals(dayStats1.hashCode(), dayStats3.hashCode());
- dayStats4.log(100, 7);
- assertEquals(dayStats3, dayStats4);
- assertEquals(dayStats3.hashCode(), dayStats4.hashCode());
- }
-
- @Test
- public void testAmbientBrightnessDayStatsIncorrectInit() {
- try {
- new AmbientBrightnessDayStats(LocalDate.now(), new float[]{1, 10, 100},
- new float[]{1, 5, 6, 7});
- } catch (IllegalArgumentException e) {
- // Expected
- }
- try {
- new AmbientBrightnessDayStats(LocalDate.now(), new float[]{});
- } catch (IllegalArgumentException e) {
- // Expected
- }
+ public void testGetters() {
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, STATS);
+ assertEquals(LOCAL_DATE, dayStats.getLocalDate());
+ assertArrayEquals(BUCKET_BOUNDARIES, dayStats.getBucketBoundaries(), 0);
+ assertArrayEquals(STATS, dayStats.getStats(), 0);
}
@Test
@@ -100,4 +110,62 @@
parcel);
assertEquals(stats, statsAgain);
}
+
+ @Test
+ public void testAmbientBrightnessDayStatsEquals() {
+ AmbientBrightnessDayStats emptyDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES);
+ AmbientBrightnessDayStats identicalEmptyDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, new float[BUCKET_BOUNDARIES.length]);
+ assertEquals(emptyDayStats, identicalEmptyDayStats);
+ assertEquals(emptyDayStats.hashCode(), identicalEmptyDayStats.hashCode());
+
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, STATS);
+ AmbientBrightnessDayStats identicalDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, STATS);
+ assertEquals(dayStats, identicalDayStats);
+ assertEquals(dayStats.hashCode(), identicalDayStats.hashCode());
+
+ assertNotEquals(emptyDayStats, dayStats);
+ assertNotEquals(emptyDayStats.hashCode(), dayStats.hashCode());
+
+ AmbientBrightnessDayStats differentDateDayStats = new AmbientBrightnessDayStats(
+ LOCAL_DATE.plusDays(1), BUCKET_BOUNDARIES, STATS);
+ assertNotEquals(dayStats, differentDateDayStats);
+ assertNotEquals(dayStats.hashCode(), differentDateDayStats.hashCode());
+
+ float[] differentStats = Arrays.copyOf(STATS, STATS.length);
+ differentStats[differentStats.length - 1] += 5f;
+ AmbientBrightnessDayStats differentStatsDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, differentStats);
+ assertNotEquals(dayStats, differentDateDayStats);
+ assertNotEquals(dayStats.hashCode(), differentStatsDayStats.hashCode());
+
+ float[] differentBucketBoundaries = Arrays.copyOf(BUCKET_BOUNDARIES,
+ BUCKET_BOUNDARIES.length);
+ differentBucketBoundaries[differentBucketBoundaries.length - 1] += 100f;
+ AmbientBrightnessDayStats differentBoundariesDayStats = new AmbientBrightnessDayStats(
+ LOCAL_DATE, differentBucketBoundaries, STATS);
+ assertNotEquals(dayStats, differentBoundariesDayStats);
+ assertNotEquals(dayStats.hashCode(), differentBoundariesDayStats.hashCode());
+ }
+
+ private interface ExceptionRunnable {
+ void run() throws Exception;
+ }
+
+ private static void assertThrows(Class<? extends Throwable> exceptionClass,
+ ExceptionRunnable r) {
+ try {
+ r.run();
+ } catch (Throwable e) {
+ assertTrue("Expected exception type " + exceptionClass.getName() + " but got "
+ + e.getClass().getName(), exceptionClass.isAssignableFrom(e.getClass()));
+ return;
+ }
+ fail("Expected exception type " + exceptionClass.getName()
+ + ", but no exception was thrown");
+ }
+
}
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index 27b7f9e..ea0347d 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -192,6 +192,12 @@
assertEquals("a:a@example.com:a@example2.com", uri.getAuthority());
assertEquals("example2.com", uri.getHost());
assertEquals(-1, uri.getPort());
+ assertEquals("/path", uri.getPath());
+
+ uri = Uri.parse("http://a.foo.com\\.example.com/path");
+ assertEquals("a.foo.com", uri.getHost());
+ assertEquals(-1, uri.getPort());
+ assertEquals("\\.example.com/path", uri.getPath());
}
@SmallTest
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index cd20192..0bc3a2d 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -21,16 +21,19 @@
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.os.FileUtils.MemoryPipe;
import android.provider.DocumentsContract.Document;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import libcore.io.IoUtils;
+import libcore.io.Streams;
import com.google.android.collect.Sets;
@@ -40,11 +43,13 @@
import org.junit.runner.RunWith;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.FileWriter;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.Random;
@RunWith(AndroidJUnit4.class)
public class FileUtilsTest {
@@ -56,6 +61,8 @@
private File mCopyFile;
private File mTarget;
+ private final int[] DATA_SIZES = { 32, 32_000, 32_000_000 };
+
private Context getContext() {
return InstrumentationRegistry.getContext();
}
@@ -80,7 +87,7 @@
@Test
public void testCopyFile() throws Exception {
- stageFile(mTestFile, TEST_DATA);
+ writeFile(mTestFile, TEST_DATA);
assertFalse(mCopyFile.exists());
FileUtils.copyFile(mTestFile, mCopyFile);
assertTrue(mCopyFile.exists());
@@ -97,6 +104,104 @@
}
@Test
+ public void testCopy_FileToFile() throws Exception {
+ for (int size : DATA_SIZES) {
+ final File src = new File(mTarget, "src");
+ final File dest = new File(mTarget, "dest");
+
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+ writeFile(src, expected);
+
+ try (FileInputStream in = new FileInputStream(src);
+ FileOutputStream out = new FileOutputStream(dest)) {
+ FileUtils.copy(in, out);
+ }
+
+ actual = readFile(dest);
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_FileToPipe() throws Exception {
+ for (int size : DATA_SIZES) {
+ final File src = new File(mTarget, "src");
+
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+ writeFile(src, expected);
+
+ try (FileInputStream in = new FileInputStream(src);
+ MemoryPipe out = MemoryPipe.createSink(actual)) {
+ FileUtils.copy(in.getFD(), out.getFD());
+ out.join();
+ }
+
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_PipeToFile() throws Exception {
+ for (int size : DATA_SIZES) {
+ final File dest = new File(mTarget, "dest");
+
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+
+ try (MemoryPipe in = MemoryPipe.createSource(expected);
+ FileOutputStream out = new FileOutputStream(dest)) {
+ FileUtils.copy(in.getFD(), out.getFD());
+ }
+
+ actual = readFile(dest);
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_PipeToPipe() throws Exception {
+ for (int size : DATA_SIZES) {
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+
+ try (MemoryPipe in = MemoryPipe.createSource(expected);
+ MemoryPipe out = MemoryPipe.createSink(actual)) {
+ FileUtils.copy(in.getFD(), out.getFD());
+ out.join();
+ }
+
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_ShortPipeToFile() throws Exception {
+ byte[] source = new byte[33_000_000];
+ new Random().nextBytes(source);
+
+ for (int size : DATA_SIZES) {
+ final File dest = new File(mTarget, "dest");
+
+ byte[] expected = Arrays.copyOf(source, size);
+ byte[] actual = new byte[size];
+
+ try (MemoryPipe in = MemoryPipe.createSource(source);
+ FileOutputStream out = new FileOutputStream(dest)) {
+ FileUtils.copy(in.getFD(), out.getFD(), null, null, size);
+ }
+
+ actual = readFile(dest);
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
public void testIsFilenameSafe() throws Exception {
assertTrue(FileUtils.isFilenameSafe(new File("foobar")));
assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23")));
@@ -106,7 +211,7 @@
@Test
public void testReadTextFile() throws Exception {
- stageFile(mTestFile, TEST_DATA);
+ writeFile(mTestFile, TEST_DATA);
assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 0, null));
@@ -127,7 +232,7 @@
@Test
public void testReadTextFileWithZeroLengthFile() throws Exception {
- stageFile(mTestFile, TEST_DATA);
+ writeFile(mTestFile, TEST_DATA);
new FileOutputStream(mTestFile).close(); // Zero out the file
assertEquals("", FileUtils.readTextFile(mTestFile, 0, null));
assertEquals("", FileUtils.readTextFile(mTestFile, 1, "<>"));
@@ -381,12 +486,21 @@
file.setLastModified(System.currentTimeMillis() - age);
}
- private void stageFile(File file, String data) throws Exception {
- FileWriter writer = new FileWriter(file);
- try {
- writer.write(data, 0, data.length());
- } finally {
- writer.close();
+ private void writeFile(File file, String data) throws Exception {
+ writeFile(file, data.getBytes());
+ }
+
+ private void writeFile(File file, byte[] data) throws Exception {
+ try (FileOutputStream out = new FileOutputStream(file)) {
+ out.write(data);
+ }
+ }
+
+ private byte[] readFile(File file) throws Exception {
+ try (FileInputStream in = new FileInputStream(file);
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ Streams.copy(in, out);
+ return out.toByteArray();
}
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 8addffbb..a678c4d 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -157,6 +157,7 @@
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.PACKAGE_USAGE_STATS"/>
<permission name="android.permission.PERFORM_CDMA_PROVISIONING"/>
<permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
@@ -227,6 +228,7 @@
<permission name="android.permission.CALL_PRIVILEGED"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MODIFY_AUDIO_ROUTING" />
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index dad24da..22867df 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -333,6 +333,9 @@
<family lang="und-Cari">
<font weight="400" style="normal">NotoSansCarian-Regular.ttf</font>
</family>
+ <family lang="und-Cakm">
+ <font weight="400" style="normal">NotoSansChakma-Regular.ttf</font>
+ </family>
<family lang="und-Cher">
<font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
</family>
@@ -429,6 +432,9 @@
<family lang="und-Orkh">
<font weight="400" style="normal">NotoSansOldTurkic-Regular.ttf</font>
</family>
+ <family lang="und-Osge">
+ <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font>
+ </family>
<family lang="und-Osma">
<font weight="400" style="normal">NotoSansOsmanya-Regular.ttf</font>
</family>
diff --git a/docs/html/reference/images/text/style/drawablemarginspan.png b/docs/html/reference/images/text/style/drawablemarginspan.png
new file mode 100644
index 0000000..edf926d
--- /dev/null
+++ b/docs/html/reference/images/text/style/drawablemarginspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/dynamicdrawablespan.png b/docs/html/reference/images/text/style/dynamicdrawablespan.png
new file mode 100644
index 0000000..8776b03
--- /dev/null
+++ b/docs/html/reference/images/text/style/dynamicdrawablespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/iconmarginspan.png b/docs/html/reference/images/text/style/iconmarginspan.png
new file mode 100644
index 0000000..8ec39be
--- /dev/null
+++ b/docs/html/reference/images/text/style/iconmarginspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/imagespan.png b/docs/html/reference/images/text/style/imagespan.png
new file mode 100644
index 0000000..c03e6bb
--- /dev/null
+++ b/docs/html/reference/images/text/style/imagespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/maskfilterspan.png b/docs/html/reference/images/text/style/maskfilterspan.png
new file mode 100644
index 0000000..6e55dbc
--- /dev/null
+++ b/docs/html/reference/images/text/style/maskfilterspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/stylespan.png b/docs/html/reference/images/text/style/stylespan.png
new file mode 100644
index 0000000..9ffa05b
--- /dev/null
+++ b/docs/html/reference/images/text/style/stylespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/tabstopspan.png b/docs/html/reference/images/text/style/tabstopspan.png
new file mode 100644
index 0000000..89a1121
--- /dev/null
+++ b/docs/html/reference/images/text/style/tabstopspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/typefacespan.png b/docs/html/reference/images/text/style/typefacespan.png
new file mode 100644
index 0000000..67e2cf9
--- /dev/null
+++ b/docs/html/reference/images/text/style/typefacespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/urlspan.png b/docs/html/reference/images/text/style/urlspan.png
new file mode 100644
index 0000000..1134520
--- /dev/null
+++ b/docs/html/reference/images/text/style/urlspan.png
Binary files differ
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 627d551..69a5874 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -541,10 +541,19 @@
return mAllowHwBitmapsInSwMode;
}
+ /**
+ * @hide
+ */
+ protected void onHwBitmapInSwMode() {
+ if (!mAllowHwBitmapsInSwMode) {
+ throw new IllegalArgumentException(
+ "Software rendering doesn't support hardware bitmaps");
+ }
+ }
+
private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
- if (!mAllowHwBitmapsInSwMode && !isHardwareAccelerated()
- && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
- throw new IllegalStateException("Software rendering doesn't support hardware bitmaps");
+ if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
+ onHwBitmapInSwMode();
}
}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 0072012..44e7066 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -29,6 +29,10 @@
import android.os.Trace;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
+
import libcore.util.NativeAllocationRegistry;
import java.io.OutputStream;
@@ -1171,6 +1175,82 @@
}
/**
+ * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+ *
+ * Equivalent to calling {@link #createBitmap(Picture, int, int, Config)} with
+ * width and height the same as the Picture's width and height and a Config.HARDWARE
+ * config.
+ *
+ * @param source The recorded {@link Picture} of drawing commands that will be
+ * drawn into the returned Bitmap.
+ * @return An immutable bitmap with a HARDWARE config whose contents are created
+ * from the recorded drawing commands in the Picture source.
+ */
+ public static @NonNull Bitmap createBitmap(@NonNull Picture source) {
+ return createBitmap(source, source.getWidth(), source.getHeight(), Config.HARDWARE);
+ }
+
+ /**
+ * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+ *
+ * The bitmap will be immutable with the given width and height. If the width and height
+ * are not the same as the Picture's width & height, the Picture will be scaled to
+ * fit the given width and height.
+ *
+ * @param source The recorded {@link Picture} of drawing commands that will be
+ * drawn into the returned Bitmap.
+ * @param width The width of the bitmap to create. The picture's width will be
+ * scaled to match if necessary.
+ * @param height The height of the bitmap to create. The picture's height will be
+ * scaled to match if necessary.
+ * @param config The {@link Config} of the created bitmap. If this is null then
+ * the bitmap will be {@link Config#HARDWARE}.
+ *
+ * @return An immutable bitmap with a HARDWARE config whose contents are created
+ * from the recorded drawing commands in the Picture source.
+ */
+ public static @NonNull Bitmap createBitmap(@NonNull Picture source, int width, int height,
+ @NonNull Config config) {
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("width & height must be > 0");
+ }
+ if (config == null) {
+ throw new IllegalArgumentException("Config must not be null");
+ }
+ if (source.requiresHardwareAcceleration() && config != Config.HARDWARE) {
+ StrictMode.noteSlowCall("GPU readback");
+ }
+ if (config == Config.HARDWARE || source.requiresHardwareAcceleration()) {
+ final RenderNode node = RenderNode.create("BitmapTemporary", null);
+ node.setLeftTopRightBottom(0, 0, width, height);
+ node.setClipToBounds(false);
+ final DisplayListCanvas canvas = node.start(width, height);
+ if (source.getWidth() != width || source.getHeight() != height) {
+ canvas.scale(width / (float) source.getWidth(),
+ height / (float) source.getHeight());
+ }
+ canvas.drawPicture(source);
+ node.end(canvas);
+ Bitmap bitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
+ if (config != Config.HARDWARE) {
+ bitmap = bitmap.copy(config, false);
+ }
+ return bitmap;
+ } else {
+ Bitmap bitmap = Bitmap.createBitmap(width, height, config);
+ Canvas canvas = new Canvas(bitmap);
+ if (source.getWidth() != width || source.getHeight() != height) {
+ canvas.scale(width / (float) source.getWidth(),
+ height / (float) source.getHeight());
+ }
+ canvas.drawPicture(source);
+ canvas.setBitmap(null);
+ bitmap.makeImmutable();
+ return bitmap;
+ }
+ }
+
+ /**
* Returns an optional array of private data, used by the UI system for
* some bitmaps. Not intended to be called by applications.
*/
@@ -1259,6 +1339,12 @@
return mIsMutable;
}
+ /** @hide */
+ public final void makeImmutable() {
+ // todo mIsMutable = false;
+ // todo nMakeImmutable();
+ }
+
/**
* <p>Indicates whether pixels stored in this bitmaps are stored pre-multiplied.
* When a pixel is pre-multiplied, the RGB components have been multiplied by
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index d77e601..fe2b523 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -160,25 +160,6 @@
isItalic);
}
- /**
- * Allow creating unsupported FontFamily.
- *
- * For compatibility reasons, we still need to create a FontFamily object even if Minikin failed
- * to find any usable 'cmap' table for some reasons, e.g. broken 'cmap' table, no 'cmap' table
- * encoded with Unicode code points, etc. Without calling this method, the freeze() method will
- * return null if Minikin fails to find any usable 'cmap' table. By calling this method, the
- * freeze() won't fail and will create an empty FontFamily. This empty FontFamily is placed at
- * the top of the fallback chain but is never used. if we don't create this empty FontFamily
- * and put it at top, bad things (performance regressions, unexpected glyph selection) will
- * happen.
- */
- public void allowUnsupportedFont() {
- if (mBuilderPtr == 0) {
- throw new IllegalStateException("Unable to allow unsupported font.");
- }
- nAllowUnsupportedFont(mBuilderPtr);
- }
-
// TODO: Remove once internal user stop using private API.
private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
return nAddFont(builderPtr, font, ttcIndex, -1, -1);
@@ -190,9 +171,6 @@
private static native long nCreateFamily(long mBuilderPtr);
@CriticalNative
- private static native void nAllowUnsupportedFont(long builderPtr);
-
- @CriticalNative
private static native void nAbort(long mBuilderPtr);
@CriticalNative
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index bbf2145..7405664 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -438,6 +438,7 @@
private boolean mPreferRamOverQuality = false;
private boolean mAsAlphaMask = false;
private Rect mCropRect;
+ private Rect mOutPaddingRect;
private Source mSource;
private PostProcessor mPostProcessor;
@@ -765,6 +766,18 @@
}
/**
+ * Set a Rect for retrieving nine patch padding.
+ *
+ * If the image is a nine patch, this Rect will be set to the padding
+ * rectangle during decode. Otherwise it will not be modified.
+ *
+ * @hide
+ */
+ public void setOutPaddingRect(@NonNull Rect outPadding) {
+ mOutPaddingRect = outPadding;
+ }
+
+ /**
* Specify whether the {@link Bitmap} should be mutable.
*
* <p>By default, a {@link Bitmap} created will be immutable, but that can
@@ -875,7 +888,6 @@
postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
mMutable, mAllocator, mRequireUnpremultiplied,
mPreferRamOverQuality, mAsAlphaMask);
-
}
private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
@@ -948,7 +960,10 @@
if (np != null && NinePatch.isNinePatchChunk(np)) {
Rect opticalInsets = new Rect();
bm.getOpticalInsets(opticalInsets);
- Rect padding = new Rect();
+ Rect padding = decoder.mOutPaddingRect;
+ if (padding == null) {
+ padding = new Rect();
+ }
nGetPadding(decoder.mNativePtr, padding);
return new NinePatchDrawable(res, bm, np, padding,
opticalInsets, null);
@@ -991,6 +1006,15 @@
final int srcDensity = computeDensity(src, decoder);
Bitmap bm = decoder.decodeBitmap();
bm.setDensity(srcDensity);
+
+ Rect padding = decoder.mOutPaddingRect;
+ if (padding != null) {
+ byte[] np = bm.getNinePatchChunk();
+ if (np != null && NinePatch.isNinePatchChunk(np)) {
+ nGetPadding(decoder.mNativePtr, padding);
+ }
+ }
+
return bm;
}
}
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 08eeaff..9ac94d8 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -31,8 +31,9 @@
* be replayed on a hardware accelerated canvas.</p>
*/
public class Picture {
- private Canvas mRecordingCanvas;
+ private PictureCanvas mRecordingCanvas;
private long mNativePicture;
+ private boolean mRequiresHwAcceleration;
private static final int WORKING_STREAM_STORAGE = 16 * 1024;
@@ -78,8 +79,12 @@
* into it.
*/
public Canvas beginRecording(int width, int height) {
+ if (mRecordingCanvas != null) {
+ throw new IllegalStateException("Picture already recording, must call #endRecording()");
+ }
long ni = nativeBeginRecording(mNativePicture, width, height);
- mRecordingCanvas = new RecordingCanvas(this, ni);
+ mRecordingCanvas = new PictureCanvas(this, ni);
+ mRequiresHwAcceleration = false;
return mRecordingCanvas;
}
@@ -91,6 +96,7 @@
*/
public void endRecording() {
if (mRecordingCanvas != null) {
+ mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
mRecordingCanvas = null;
nativeEndRecording(mNativePicture);
}
@@ -113,6 +119,18 @@
}
/**
+ * Indicates whether or not this Picture contains recorded commands that only work when
+ * drawn to a hardware-accelerated canvas. If this returns true then this Picture can only
+ * be drawn to another Picture or to a Canvas where canvas.isHardwareAccelerated() is true.
+ *
+ * @return true if the Picture can only be drawn to a hardware-accelerated canvas,
+ * false otherwise.
+ */
+ public boolean requiresHardwareAcceleration() {
+ return mRequiresHwAcceleration;
+ }
+
+ /**
* Draw this picture on the canvas.
* <p>
* Prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this call could
@@ -129,6 +147,9 @@
if (mRecordingCanvas != null) {
endRecording();
}
+ if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) {
+ canvas.onHwBitmapInSwMode();
+ }
nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
}
@@ -164,8 +185,7 @@
if (stream == null) {
throw new NullPointerException();
}
- if (!nativeWriteToStream(mNativePicture, stream,
- new byte[WORKING_STREAM_STORAGE])) {
+ if (!nativeWriteToStream(mNativePicture, stream, new byte[WORKING_STREAM_STORAGE])) {
throw new RuntimeException();
}
}
@@ -182,10 +202,11 @@
OutputStream stream, byte[] storage);
private static native void nativeDestructor(long nativePicture);
- private static class RecordingCanvas extends Canvas {
+ private static class PictureCanvas extends Canvas {
private final Picture mPicture;
+ boolean mHoldsHwBitmap;
- public RecordingCanvas(Picture pict, long nativeCanvas) {
+ public PictureCanvas(Picture pict, long nativeCanvas) {
super(nativeCanvas);
mPicture = pict;
}
@@ -202,5 +223,10 @@
}
super.drawPicture(picture);
}
+
+ @Override
+ protected void onHwBitmapInSwMode() {
+ mHoldsHwBitmap = true;
+ }
}
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index ef41507..04c5295 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -818,12 +818,9 @@
if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
null /* axes */)) {
- // Due to backward compatibility, even if the font is not supported by our font
- // stack, we need to place the empty font at the first place. The typeface with
- // empty font behaves different from default typeface especially in fallback
- // font selection.
- fontFamily.allowUnsupportedFont();
- fontFamily.freeze();
+ if (!fontFamily.freeze()) {
+ return Typeface.DEFAULT;
+ }
final FontFamily[] families = { fontFamily };
typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
@@ -870,12 +867,9 @@
final FontFamily fontFamily = new FontFamily();
if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
- // Due to backward compatibility, even if the font is not supported by our font
- // stack, we need to place the empty font at the first place. The typeface with
- // empty font behaves different from default typeface especially in fallback font
- // selection.
- fontFamily.allowUnsupportedFont();
- fontFamily.freeze();
+ if (!fontFamily.freeze()) {
+ return Typeface.DEFAULT;
+ }
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index bd49b87..27c8fda 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -348,7 +348,9 @@
if (mState == null) {
throw new IllegalStateException("called stop on empty AnimatedImageDrawable");
}
- nStop(mState.mNativePtr);
+ if (nStop(mState.mNativePtr)) {
+ postOnAnimationEnd();
+ }
}
// Animatable2 overrides
@@ -365,21 +367,31 @@
nSetOnAnimationEndListener(mState.mNativePtr, this);
}
- mAnimationCallbacks.add(callback);
+ if (!mAnimationCallbacks.contains(callback)) {
+ mAnimationCallbacks.add(callback);
+ }
}
@Override
public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) {
- if (callback == null || mAnimationCallbacks == null) {
+ if (callback == null || mAnimationCallbacks == null
+ || !mAnimationCallbacks.remove(callback)) {
return false;
}
- return mAnimationCallbacks.remove(callback);
+ if (mAnimationCallbacks.isEmpty()) {
+ clearAnimationCallbacks();
+ }
+
+ return true;
}
@Override
public void clearAnimationCallbacks() {
- mAnimationCallbacks = null;
+ if (mAnimationCallbacks != null) {
+ mAnimationCallbacks = null;
+ nSetOnAnimationEndListener(mState.mNativePtr, null);
+ }
}
private void postOnAnimationStart() {
@@ -413,6 +425,21 @@
return mHandler;
}
+ /**
+ * Called by JNI.
+ *
+ * The JNI code has already posted this to the thread that created the
+ * callback, so no need to post.
+ */
+ @SuppressWarnings("unused")
+ private void onAnimationEnd() {
+ if (mAnimationCallbacks != null) {
+ for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
+ callback.onAnimationEnd(this);
+ }
+ }
+ }
+
private static native long nCreate(long nativeImageDecoder,
@Nullable ImageDecoder decoder, int width, int height, Rect cropRect)
@@ -432,7 +459,7 @@
@FastNative
private static native boolean nStart(long nativePtr);
@FastNative
- private static native void nStop(long nativePtr);
+ private static native boolean nStop(long nativePtr);
@FastNative
private static native void nSetLoopCount(long nativePtr, int loopCount);
// Pass the drawable down to native so it can call onAnimationEnd.
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 7ad062a..44b783b 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -27,6 +27,7 @@
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Outline;
@@ -49,6 +50,7 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -111,7 +113,7 @@
*/
@Deprecated
public BitmapDrawable() {
- mBitmapState = new BitmapState((Bitmap) null);
+ init(new BitmapState((Bitmap) null), null);
}
/**
@@ -124,8 +126,7 @@
@SuppressWarnings("unused")
@Deprecated
public BitmapDrawable(Resources res) {
- mBitmapState = new BitmapState((Bitmap) null);
- mBitmapState.mTargetDensity = mTargetDensity;
+ init(new BitmapState((Bitmap) null), res);
}
/**
@@ -135,7 +136,7 @@
*/
@Deprecated
public BitmapDrawable(Bitmap bitmap) {
- this(new BitmapState(bitmap), null);
+ init(new BitmapState(bitmap), null);
}
/**
@@ -143,8 +144,7 @@
* the display metrics of the resources.
*/
public BitmapDrawable(Resources res, Bitmap bitmap) {
- this(new BitmapState(bitmap), res);
- mBitmapState.mTargetDensity = mTargetDensity;
+ init(new BitmapState(bitmap), res);
}
/**
@@ -154,10 +154,7 @@
*/
@Deprecated
public BitmapDrawable(String filepath) {
- this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
- }
+ this(null, filepath);
}
/**
@@ -165,10 +162,21 @@
*/
@SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
public BitmapDrawable(Resources res, String filepath) {
- this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
- mBitmapState.mTargetDensity = mTargetDensity;
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+ Bitmap bitmap = null;
+ try (FileInputStream stream = new FileInputStream(filepath)) {
+ bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream),
+ (decoder, info, src) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (Exception e) {
+ /* do nothing. This matches the behavior of BitmapFactory.decodeFile()
+ If the exception happened on decode, mBitmapState.mBitmap will be null.
+ */
+ } finally {
+ init(new BitmapState(bitmap), res);
+ if (mBitmapState.mBitmap == null) {
+ android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+ }
}
}
@@ -179,10 +187,7 @@
*/
@Deprecated
public BitmapDrawable(java.io.InputStream is) {
- this(new BitmapState(BitmapFactory.decodeStream(is)), null);
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
- }
+ this(null, is);
}
/**
@@ -190,10 +195,21 @@
*/
@SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
public BitmapDrawable(Resources res, java.io.InputStream is) {
- this(new BitmapState(BitmapFactory.decodeStream(is)), null);
- mBitmapState.mTargetDensity = mTargetDensity;
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+ Bitmap bitmap = null;
+ try {
+ bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is),
+ (decoder, info, src) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (Exception e) {
+ /* do nothing. This matches the behavior of BitmapFactory.decodeStream()
+ If the exception happened on decode, mBitmapState.mBitmap will be null.
+ */
+ } finally {
+ init(new BitmapState(bitmap), res);
+ if (mBitmapState.mBitmap == null) {
+ android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+ }
}
}
@@ -812,9 +828,19 @@
}
}
+ int density = Bitmap.DENSITY_NONE;
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ density = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ density = value.density;
+ }
+
Bitmap bitmap = null;
try (InputStream is = r.openRawResource(srcResId, value)) {
- bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null);
+ ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+ bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
} catch (Exception e) {
// Do nothing and pick up the error below.
}
@@ -1013,14 +1039,21 @@
}
}
+ private BitmapDrawable(BitmapState state, Resources res) {
+ init(state, res);
+ }
+
/**
- * The one constructor to rule them all. This is called by all public
+ * The one helper to rule them all. This is called by all public & private
* constructors to set the state and initialize local properties.
*/
- private BitmapDrawable(BitmapState state, Resources res) {
+ private void init(BitmapState state, Resources res) {
mBitmapState = state;
-
updateLocalState(res);
+
+ if (mBitmapState != null && res != null) {
+ mBitmapState.mTargetDensity = mTargetDensity;
+ }
}
/**
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index f17cd76..36a4d26 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -37,6 +37,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.NinePatch;
import android.graphics.Outline;
@@ -50,11 +51,13 @@
import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.util.StateSet;
import android.util.TypedValue;
import android.util.Xml;
import android.view.View;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
@@ -1175,6 +1178,10 @@
return null;
}
+ if (opts == null) {
+ return getBitmapDrawable(res, value, is);
+ }
+
/* ugh. The decodeStream contract is that we have already allocated
the pad rect, but if the bitmap does not had a ninepatch chunk,
then the pad will be ignored. If we could change this to lazily
@@ -1207,6 +1214,33 @@
return null;
}
+ private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) {
+ try {
+ ImageDecoder.Source source = null;
+ if (value != null) {
+ int density = Bitmap.DENSITY_NONE;
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ density = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ density = value.density;
+ }
+ source = ImageDecoder.createSource(res, is, density);
+ } else {
+ source = ImageDecoder.createSource(res, is);
+ }
+
+ return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (IOException e) {
+ /* do nothing.
+ If the exception happened on decode, the drawable will be null.
+ */
+ Log.e("Drawable", "Unable to decode stream: " + e);
+ }
+ return null;
+ }
+
/**
* Create a drawable from an XML document. For more information on how to
* create resources in XML, see
@@ -1306,11 +1340,10 @@
}
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);
- try {
- Bitmap bm = BitmapFactory.decodeFile(pathName);
- if (bm != null) {
- return drawableFromBitmap(null, bm, null, null, null, pathName);
- }
+ try (FileInputStream stream = new FileInputStream(pathName)) {
+ return getBitmapDrawable(null, null, stream);
+ } catch(IOException e) {
+ // Do nothing; we will just return null if the FileInputStream had an error
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 1790020..a56e8d1 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -24,9 +24,9 @@
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.NinePatch;
import android.graphics.Outline;
@@ -211,7 +211,8 @@
restoreAlpha = -1;
}
- final boolean needsDensityScaling = canvas.getDensity() == 0;
+ final boolean needsDensityScaling = canvas.getDensity() == 0
+ && Bitmap.DENSITY_NONE != state.mNinePatch.getDensity();
if (needsDensityScaling) {
restoreToCount = restoreToCount >= 0 ? restoreToCount : canvas.save();
@@ -421,10 +422,6 @@
final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
if (srcResId != 0) {
- final BitmapFactory.Options options = new BitmapFactory.Options();
- options.inDither = !state.mDither;
- options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
-
final Rect padding = new Rect();
final Rect opticalInsets = new Rect();
Bitmap bitmap = null;
@@ -433,7 +430,17 @@
final TypedValue value = new TypedValue();
final InputStream is = r.openRawResource(srcResId, value);
- bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
+ int density = Bitmap.DENSITY_NONE;
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ density = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ density = value.density;
+ }
+ ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+ bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+ decoder.setOutPaddingRect(padding);
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
is.close();
} catch (IOException e) {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 3323bce..24d819e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -134,6 +134,8 @@
name: "libhwui_defaults",
defaults: ["hwui_defaults"],
+ shared_libs: ["libstatslog"],
+
whole_static_libs: ["libskia"],
srcs: [
@@ -318,7 +320,10 @@
"libgmock",
"libhwui_static_debug",
],
- shared_libs: ["libmemunreachable"],
+ shared_libs: [
+ "libmemunreachable",
+ "libstatslog",
+ ],
cflags: [
"-include debug/wrap_gles.h",
"-DHWUI_NULL_GPU",
@@ -383,7 +388,10 @@
// set to libhwui_static_debug to skip actual GL commands
whole_static_libs: ["libhwui"],
- shared_libs: ["libmemunreachable"],
+ shared_libs: [
+ "libmemunreachable",
+ "libstatslog",
+ ],
srcs: [
"tests/macrobench/TestSceneRunner.cpp",
@@ -405,7 +413,10 @@
],
whole_static_libs: ["libhwui_static_debug"],
- shared_libs: ["libmemunreachable"],
+ shared_libs: [
+ "libmemunreachable",
+ "libstatslog",
+ ],
srcs: [
"tests/microbench/main.cpp",
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index f41956c..ab27a0d 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -18,6 +18,7 @@
#include <errno.h>
#include <inttypes.h>
+#include <statslog.h>
#include <sys/mman.h>
#include <algorithm>
@@ -164,6 +165,7 @@
ALOGI("%s", ss.str().c_str());
// Just so we have something that counts up, the value is largely irrelevant
ATRACE_INT(ss.str().c_str(), ++sDaveyCount);
+ android::util::stats_write(android::util::DAVEY_OCCURRED, ns2ms(totalDuration));
}
}
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 5356d3b..2bded9b 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -48,8 +48,10 @@
return true;
}
-void AnimatedImageDrawable::stop() {
+bool AnimatedImageDrawable::stop() {
+ bool wasRunning = mRunning;
mRunning = false;
+ return wasRunning;
}
bool AnimatedImageDrawable::isRunning() {
@@ -180,7 +182,6 @@
if (finalFrame) {
if (mEndListener) {
mEndListener->onAnimationEnd();
- mEndListener = nullptr;
}
}
}
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index 9d84ed5..2fd6f40 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -68,7 +68,9 @@
// Returns true if the animation was started; false otherwise (e.g. it was
// already running)
bool start();
- void stop();
+ // Returns true if the animation was stopped; false otherwise (e.g. it was
+ // already stopped)
+ bool stop();
bool isRunning();
void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); }
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 43f46ef..a0d000d 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -44,7 +44,6 @@
minikinPaint.familyVariant = paint->getFamilyVariant();
minikinPaint.fontStyle = resolvedFace->fStyle;
minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
- minikinPaint.hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit());
return minikinPaint;
}
@@ -55,18 +54,23 @@
minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
minikin::Layout layout;
+ const minikin::U16StringPiece textBuf(buf, bufSize);
+ const minikin::Range range(start, start + count);
+ const minikin::HyphenEdit hyphenEdit = static_cast<minikin::HyphenEdit>(paint->getHyphenEdit());
+ const minikin::StartHyphenEdit startHyphen = minikin::startHyphenEdit(hyphenEdit);
+ const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit);
+
if (mt == nullptr) {
- layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint);
+ layout.doLayout(textBuf,range, bidiFlags, minikinPaint, startHyphen, endHyphen);
return layout;
}
- if (mt->buildLayout(minikin::U16StringPiece(buf, bufSize),
- minikin::Range(start, start + count),
- minikinPaint, bidiFlags, mtOffset, &layout)) {
+ if (mt->buildLayout(textBuf, range, minikinPaint, bidiFlags, mtOffset, startHyphen, endHyphen,
+ &layout)) {
return layout;
}
- layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint);
+ layout.doLayout(textBuf, range, bidiFlags, minikinPaint, startHyphen, endHyphen);
return layout;
}
@@ -74,8 +78,14 @@
const Typeface* typeface, const uint16_t* buf, size_t start,
size_t count, size_t bufSize, float* advances) {
minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
- return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinPaint,
- advances, nullptr /* extent */);
+ const minikin::U16StringPiece textBuf(buf, bufSize);
+ const minikin::Range range(start, start + count);
+ const minikin::HyphenEdit hyphenEdit = static_cast<minikin::HyphenEdit>(paint->getHyphenEdit());
+ const minikin::StartHyphenEdit startHyphen = minikin::startHyphenEdit(hyphenEdit);
+ const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit);
+
+ return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen,
+ endHyphen, advances, nullptr /* extent */);
}
bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) {
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index ebc14c8..091b526 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -182,10 +182,11 @@
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
- std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
- std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())}));
- std::shared_ptr<minikin::FontCollection> collection =
- std::make_shared<minikin::FontCollection>(std::move(family));
+ std::vector<minikin::Font> fonts;
+ fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
+
+ std::shared_ptr<minikin::FontCollection> collection = std::make_shared<minikin::FontCollection>(
+ std::make_shared<minikin::FontFamily>(std::move(fonts)));
Typeface* hwTypeface = new Typeface();
hwTypeface->fFontCollection = collection;
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 66d6f52..2232c25 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -56,8 +56,9 @@
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
- return std::make_shared<minikin::FontFamily>(
- std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())}));
+ std::vector<minikin::Font> fonts;
+ fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
+ return std::make_shared<minikin::FontFamily>(std::move(fonts));
}
std::vector<std::shared_ptr<minikin::FontFamily>> makeSingleFamlyVector(const char* fileName) {
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 018db9a..fa3f99a 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -88,11 +88,6 @@
boolean providerMeetsCriteria(String provider, in Criteria criteria);
ProviderProperties getProviderProperties(String provider);
String getNetworkProviderPackage();
- boolean isProviderEnabled(String provider);
- boolean isProviderEnabledForUser(String provider, int userId);
- boolean setProviderEnabledForUser(String provider, boolean enabled, int userId);
- boolean isLocationEnabledForUser(int userId);
- void setLocationEnabledForUser(boolean enabled, int userId);
void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
void removeTestProvider(String provider, String opPackageName);
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index b07d042..f98480b 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -265,6 +265,12 @@
public static final int ENCODING_AAC_XHE = 16;
/** Audio data format: AC-4 sync frame transport format */
public static final int ENCODING_AC4 = 17;
+ /** Audio data format: E-AC-3-JOC compressed
+ * E-AC-3-JOC streams can be decoded by downstream devices supporting {@link #ENCODING_E_AC3}.
+ * Use {@link #ENCODING_E_AC3} as the AudioTrack encoding when the downstream device
+ * supports {@link #ENCODING_E_AC3} but not {@link #ENCODING_E_AC3_JOC}.
+ **/
+ public static final int ENCODING_E_AC3_JOC = 18;
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
@@ -512,6 +518,7 @@
case ENCODING_PCM_FLOAT:
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_MP3:
@@ -537,6 +544,7 @@
case ENCODING_PCM_FLOAT:
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
@@ -564,6 +572,7 @@
return true;
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_MP3:
@@ -593,6 +602,7 @@
return true;
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_MP3:
@@ -829,6 +839,7 @@
case ENCODING_PCM_FLOAT:
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
@@ -1044,6 +1055,7 @@
ENCODING_PCM_FLOAT,
ENCODING_AC3,
ENCODING_E_AC3,
+ ENCODING_E_AC3_JOC,
ENCODING_DTS,
ENCODING_DTS_HD,
ENCODING_IEC61937,
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index b32e539..fcdecba 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -172,6 +172,14 @@
}
/**
+ * @hide
+ */
+ @SystemApi
+ public PlaybackInfoProvider getProvider() {
+ return mProvider;
+ }
+
+ /**
* Get the type of playback which affects volume handling. One of:
* <ul>
* <li>{@link #PLAYBACK_TYPE_LOCAL}</li>
@@ -199,9 +207,9 @@
/**
* Get the type of volume control that can be used. One of:
* <ul>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
+ * <li>{@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}</li>
+ * <li>{@link VolumeProvider2#VOLUME_CONTROL_RELATIVE}</li>
+ * <li>{@link VolumeProvider2#VOLUME_CONTROL_FIXED}</li>
* </ul>
*
* @return The type of volume control that may be used with this session.
@@ -472,7 +480,7 @@
/**
* Set the volume of the output this session is playing on. The command will be ignored if it
- * does not support {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}.
+ * does not support {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
* <p>
* If the session is local playback, this changes the device's volume with the stream that
* session's player is using. Flags will be specified for the {@link AudioManager}.
@@ -494,8 +502,8 @@
* must be one of {@link AudioManager#ADJUST_LOWER},
* {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
* The command will be ignored if the session does not support
- * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
- * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}.
+ * {@link VolumeProvider2#VOLUME_CONTROL_RELATIVE} or
+ * {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
* <p>
* If the session is local playback, this changes the device's volume with the stream that
* session's player is using. Flags will be specified for the {@link AudioManager}.
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index f88f9f2..a11768e 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -27,7 +27,6 @@
import android.media.update.ApiLoader;
import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
-import android.media.update.MediaSession2Provider;
import android.media.update.MediaSessionService2Provider;
import android.os.Bundle;
@@ -63,7 +62,8 @@
public static final String SERVICE_INTERFACE = "android.media.MediaLibraryService2";
/**
- * Session for the media library service.
+ * Session for the {@link MediaLibraryService2}. Build this object with
+ * {@link MediaLibrarySessionBuilder} and return in {@link #onCreateSession(String)}.
*/
public static class MediaLibrarySession extends MediaSession2 {
private final MediaLibrarySessionProvider mProvider;
@@ -101,6 +101,9 @@
}
}
+ /**
+ * Callback for the {@link MediaLibrarySession}.
+ */
public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
public MediaLibrarySessionCallback(Context context) {
@@ -200,6 +203,8 @@
/**
* Builder for {@link MediaLibrarySession}.
*/
+ // Override all methods just to show them with the type instead of generics in Javadoc.
+ // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
public class MediaLibrarySessionBuilder extends BuilderBase<MediaLibrarySession,
MediaLibrarySessionBuilder, MediaLibrarySessionCallback> {
public MediaLibrarySessionBuilder(
@@ -210,6 +215,38 @@
context, (MediaLibrarySessionBuilder) instance, player, callbackExecutor,
callback));
}
+
+ @Override
+ public MediaLibrarySessionBuilder setVolumeProvider(
+ @Nullable VolumeProvider2 volumeProvider) {
+ return super.setVolumeProvider(volumeProvider);
+ }
+
+ @Override
+ public MediaLibrarySessionBuilder setRatingType(int type) {
+ return super.setRatingType(type);
+ }
+
+ @Override
+ public MediaLibrarySessionBuilder setSessionActivity(@Nullable PendingIntent pi) {
+ return super.setSessionActivity(pi);
+ }
+
+ @Override
+ public MediaLibrarySessionBuilder setId(String id) {
+ return super.setId(id);
+ }
+
+ @Override
+ public MediaLibrarySessionBuilder setSessionCallback(
+ @NonNull Executor executor, @NonNull MediaLibrarySessionCallback callback) {
+ return super.setSessionCallback(executor, callback);
+ }
+
+ @Override
+ public MediaLibrarySession build() {
+ return super.build();
+ }
}
@Override
@@ -229,7 +266,7 @@
* This method will be called on the main thread.
*
* @param sessionId session id written in the AndroidManifest.xml.
- * @return a new browser session
+ * @return a new library session
* @see MediaLibrarySessionBuilder
* @see #getSession()
* @throws RuntimeException if returned session is invalid
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index d84eedf..e331b2c 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -1671,35 +1671,6 @@
public abstract void deselectTrack(int index);
/**
- * Sets the target UDP re-transmit endpoint for the low level player.
- * Generally, the address portion of the endpoint is an IP multicast
- * address, although a unicast address would be equally valid. When a valid
- * retransmit endpoint has been set, the media player will not decode and
- * render the media presentation locally. Instead, the player will attempt
- * to re-multiplex its media data using the Android@Home RTP profile and
- * re-transmit to the target endpoint. Receiver devices (which may be
- * either the same as the transmitting device or different devices) may
- * instantiate, prepare, and start a receiver player using a setDataSource
- * URL of the form...
- *
- * aahRX://<multicastIP>:<port>
- *
- * to receive, decode and render the re-transmitted content.
- *
- * setRetransmitEndpoint may only be called before setDataSource has been
- * called; while the player is in the Idle state.
- *
- * @param endpoint the address and UDP port of the re-transmission target or
- * null if no re-transmission is to be performed.
- * @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if the retransmit endpoint is supplied,
- * but invalid.
- *
- * {@hide} pending API council
- */
- public void setRetransmitEndpoint(InetSocketAddress endpoint) { }
-
- /**
* Releases the resources held by this {@code MediaPlayer2} object.
*
* It is considered good practice to call this method when you're
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 222c66e..e3d5ac0 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -2964,53 +2964,6 @@
}
/**
- * Sets the target UDP re-transmit endpoint for the low level player.
- * Generally, the address portion of the endpoint is an IP multicast
- * address, although a unicast address would be equally valid. When a valid
- * retransmit endpoint has been set, the media player will not decode and
- * render the media presentation locally. Instead, the player will attempt
- * to re-multiplex its media data using the Android@Home RTP profile and
- * re-transmit to the target endpoint. Receiver devices (which may be
- * either the same as the transmitting device or different devices) may
- * instantiate, prepare, and start a receiver player using a setDataSource
- * URL of the form...
- *
- * aahRX://<multicastIP>:<port>
- *
- * to receive, decode and render the re-transmitted content.
- *
- * setRetransmitEndpoint may only be called before setDataSource has been
- * called; while the player is in the Idle state.
- *
- * @param endpoint the address and UDP port of the re-transmission target or
- * null if no re-transmission is to be performed.
- * @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if the retransmit endpoint is supplied,
- * but invalid.
- *
- * {@hide} pending API council
- */
- @Override
- public void setRetransmitEndpoint(InetSocketAddress endpoint)
- throws IllegalStateException, IllegalArgumentException
- {
- String addrString = null;
- int port = 0;
-
- if (null != endpoint) {
- addrString = endpoint.getAddress().getHostAddress();
- port = endpoint.getPort();
- }
-
- int ret = native_setRetransmitEndpoint(addrString, port);
- if (ret != 0) {
- throw new IllegalArgumentException("Illegal re-transmit endpoint; native ret " + ret);
- }
- }
-
- private native final int native_setRetransmitEndpoint(String addrString, int port);
-
- /**
* Releases the resources held by this {@code MediaPlayer2} object.
*
* It is considered good practice to call this method when you're
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 5670bd8..ae2649a 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -580,8 +580,20 @@
};
/**
- * Base builder class for MediaSession2 and its subclass.
- *
+ * Base builder class for MediaSession2 and its subclass. Any change in this class should be
+ * also applied to the subclasses {@link MediaSession2.Builder} and
+ * {@link MediaLibraryService2.MediaLibrarySessionBuilder}.
+ * <p>
+ * APIs here should be package private, but should have documentations for developers.
+ * Otherwise, javadoc will generate documentation with the generic types such as follows.
+ * <pre>U extends BuilderBase<T, U, C> setSessionCallback(Executor executor, C callback)</pre>
+ * <p>
+ * This class is hidden to prevent from generating test stub, which fails with
+ * 'unexpected bound' because it tries to auto generate stub class as follows.
+ * <pre>abstract static class BuilderBase<
+ * T extends android.media.MediaSession2,
+ * U extends android.media.MediaSession2.BuilderBase<
+ * T, U, C extends android.media.MediaSession2.SessionCallback>, C></pre>
* @hide
*/
static abstract class BuilderBase
@@ -599,9 +611,9 @@
* <p>
* Set {@code null} to reset.
*
- * @param volumeProvider The provider that will handle volume changes. Can be {@code null}
+ * @param volumeProvider The provider that will handle volume changes. Can be {@code null}.
*/
- public U setVolumeProvider(@Nullable VolumeProvider volumeProvider) {
+ U setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
mProvider.setVolumeProvider_impl(volumeProvider);
return (U) this;
}
@@ -619,7 +631,7 @@
* <li>{@link Rating2#RATING_THUMB_UP_DOWN}</li>
* </ul>
*/
- public U setRatingType(@Rating2.Style int type) {
+ U setRatingType(@Rating2.Style int type) {
mProvider.setRatingType_impl(type);
return (U) this;
}
@@ -631,7 +643,7 @@
*
* @param pi The intent to launch to show UI for this session.
*/
- public U setSessionActivity(@Nullable PendingIntent pi) {
+ U setSessionActivity(@Nullable PendingIntent pi) {
mProvider.setSessionActivity_impl(pi);
return (U) this;
}
@@ -646,7 +658,7 @@
* @throws IllegalArgumentException if id is {@code null}
* @return
*/
- public U setId(@NonNull String id) {
+ U setId(@NonNull String id) {
mProvider.setId_impl(id);
return (U) this;
}
@@ -658,7 +670,7 @@
* @param callback session callback.
* @return
*/
- public U setSessionCallback(@NonNull @CallbackExecutor Executor executor,
+ U setSessionCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull C callback) {
mProvider.setSessionCallback_impl(executor, callback);
return (U) this;
@@ -671,7 +683,7 @@
* @throws IllegalStateException if the session with the same id is already exists for the
* package.
*/
- public T build() {
+ T build() {
return mProvider.build_impl();
}
}
@@ -682,13 +694,44 @@
* Any incoming event from the {@link MediaController2} will be handled on the thread
* that created session with the {@link Builder#build()}.
*/
- // TODO(jaewan): Add setRatingType()
- // TODO(jaewan): Add setSessionActivity()
+ // Override all methods just to show them with the type instead of generics in Javadoc.
+ // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
public static final class Builder extends BuilderBase<MediaSession2, Builder, SessionCallback> {
public Builder(Context context, @NonNull MediaPlayerInterface player) {
super((instance) -> ApiLoader.getProvider(context).createMediaSession2Builder(
context, (Builder) instance, player));
}
+
+ @Override
+ public Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
+ return super.setVolumeProvider(volumeProvider);
+ }
+
+ @Override
+ public Builder setRatingType(@Rating2.Style int type) {
+ return super.setRatingType(type);
+ }
+
+ @Override
+ public Builder setSessionActivity(@Nullable PendingIntent pi) {
+ return super.setSessionActivity(pi);
+ }
+
+ @Override
+ public Builder setId(@NonNull String id) {
+ return super.setId(id);
+ }
+
+ @Override
+ public Builder setSessionCallback(@NonNull Executor executor,
+ @Nullable SessionCallback callback) {
+ return super.setSessionCallback(executor, callback);
+ }
+
+ @Override
+ public MediaSession2 build() {
+ return super.build();
+ }
}
/**
@@ -1035,8 +1078,8 @@
* If the new player is successfully set, {@link PlaybackListener}
* will be called to tell the current playback state of the new player.
* <p>
- * You can also specify a volume provider. If so, playback in the player is considered as
- * remote playback.
+ * For the remote playback case which you want to handle volume by yourself, use
+ * {@link #setPlayer(MediaPlayerInterface, VolumeProvider2)}.
*
* @param player a {@link MediaPlayerInterface} that handles actual media playback in your app.
* @throws IllegalArgumentException if the player is {@code null}.
@@ -1051,10 +1094,10 @@
* @param player a {@link MediaPlayerInterface} that handles actual media playback in your app.
* @param volumeProvider a volume provider
* @see #setPlayer(MediaPlayerInterface)
- * @see Builder#setVolumeProvider(VolumeProvider)
+ * @see Builder#setVolumeProvider(VolumeProvider2)
*/
public void setPlayer(@NonNull MediaPlayerInterface player,
- @NonNull VolumeProvider volumeProvider) {
+ @NonNull VolumeProvider2 volumeProvider) {
mProvider.setPlayer_impl(player, volumeProvider);
}
diff --git a/media/java/android/media/MicrophoneInfo.java b/media/java/android/media/MicrophoneInfo.java
index 21f9171..131e37b 100644
--- a/media/java/android/media/MicrophoneInfo.java
+++ b/media/java/android/media/MicrophoneInfo.java
@@ -55,7 +55,7 @@
/**
* Unknown microphone directionality.
*/
- public static final int DIRECTIONALITY_UNKNOW = 0;
+ public static final int DIRECTIONALITY_UNKNOWN = 0;
/**
* Microphone directionality type: omni.
@@ -104,7 +104,7 @@
/** @hide */
@IntDef(flag = true, prefix = { "DIRECTIONALITY_" }, value = {
- DIRECTIONALITY_UNKNOW,
+ DIRECTIONALITY_UNKNOWN,
DIRECTIONALITY_OMNI,
DIRECTIONALITY_BI_DIRECTIONAL,
DIRECTIONALITY_CARDIOID,
@@ -309,7 +309,7 @@
/**
* Returns the directionality of microphone. The return value is one of
- * {@link #DIRECTIONALITY_UNKNOW}, {@link #DIRECTIONALITY_OMNI},
+ * {@link #DIRECTIONALITY_UNKNOWN}, {@link #DIRECTIONALITY_OMNI},
* {@link #DIRECTIONALITY_BI_DIRECTIONAL}, {@link #DIRECTIONALITY_CARDIOID},
* {@link #DIRECTIONALITY_HYPER_CARDIOID}, or {@link #DIRECTIONALITY_SUPER_CARDIOID}.
*
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 3eb9d52..fefa1ed 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -28,11 +28,13 @@
import android.content.ContentUris;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.database.Cursor;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -47,22 +49,17 @@
import com.android.internal.database.SortCursor;
-import libcore.io.Streams;
-
import java.io.Closeable;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
-import static android.content.ContentProvider.maybeAddUserId;
-import static android.content.pm.PackageManager.NameNotFoundException;
-
/**
* RingtoneManager provides access to ringtones, notification, and other types
* of sounds. It manages querying the different media providers and combines the
@@ -855,7 +852,7 @@
final Uri cacheUri = getCacheForType(type, context.getUserId());
try (InputStream in = openRingtone(context, ringtoneUri);
OutputStream out = resolver.openOutputStream(cacheUri)) {
- Streams.copy(in, out);
+ FileUtils.copy(in, out);
} catch (IOException e) {
Log.w(TAG, "Failed to cache ringtone: " + e);
}
@@ -960,7 +957,7 @@
// Copy contents to external ringtone storage. Throws IOException if the copy fails.
try (final InputStream input = mContext.getContentResolver().openInputStream(fileUri);
final OutputStream output = new FileOutputStream(outFile)) {
- Streams.copy(input, output);
+ FileUtils.copy(input, output);
}
// Tell MediaScanner about the new file. Wait for it to assign a {@link Uri}.
diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
index 00746e2..53ba466 100644
--- a/media/java/android/media/VolumeProvider2.java
+++ b/media/java/android/media/VolumeProvider2.java
@@ -32,7 +32,7 @@
* {@link #setCurrentVolume(int)} each time the volume being provided changes.
* <p>
* You can set a volume provider on a session by calling
- * {@link MediaSession2#setPlayer(MediaPlayerInterface, VolumeProvider)}.
+ * {@link MediaSession2#setPlayer(MediaPlayerInterface, VolumeProvider2)}.
*
* @hide
*/
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 0fe7246..f2b4fe0 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -546,22 +546,39 @@
/**
* Method called when a new waveform capture is available.
* <p>Data in the waveform buffer is valid only within the scope of the callback.
- * Applications which needs access to the waveform data after returning from the callback
+ * Applications which need access to the waveform data after returning from the callback
* should make a copy of the data instead of holding a reference.
* @param visualizer Visualizer object on which the listener is registered.
* @param waveform array of bytes containing the waveform representation.
- * @param samplingRate sampling rate of the audio visualized.
+ * @param samplingRate sampling rate of the visualized audio.
*/
void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate);
/**
* Method called when a new frequency capture is available.
* <p>Data in the fft buffer is valid only within the scope of the callback.
- * Applications which needs access to the fft data after returning from the callback
+ * Applications which need access to the fft data after returning from the callback
* should make a copy of the data instead of holding a reference.
+ *
+ * <p>In order to obtain magnitude and phase values the following formulas can
+ * be used:
+ * <pre class="prettyprint">
+ * for (int i = 0; i < fft.size(); i += 2) {
+ * float magnitude = (float)Math.hypot(fft[i], fft[i + 1]);
+ * float phase = (float)Math.atan2(fft[i + 1], fft[i]);
+ * }</pre>
* @param visualizer Visualizer object on which the listener is registered.
* @param fft array of bytes containing the frequency representation.
- * @param samplingRate sampling rate of the audio visualized.
+ * The fft array only contains the first half of the actual
+ * FFT spectrum (frequencies up to Nyquist frequency), exploiting
+ * the symmetry of the spectrum. For each frequencies bin <code>i</code>:
+ * <ul>
+ * <li>the element at index <code>2*i</code> in the array contains
+ * the real part of a complex number,</li>
+ * <li>the element at index <code>2*i+1</code> contains the imaginary
+ * part of the complex number.</li>
+ * </ul>
+ * @param samplingRate sampling rate of the visualized audio.
*/
void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate);
}
diff --git a/media/java/android/media/update/FrameLayoutHelper.java b/media/java/android/media/update/FrameLayoutHelper.java
deleted file mode 100644
index 983dc70..0000000
--- a/media/java/android/media/update/FrameLayoutHelper.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.media.update;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.FrameLayout;
-
-/**
- * Helper class for connecting the public API to an updatable implementation.
- *
- * @see ViewProvider
- *
- * @hide
- */
-public abstract class FrameLayoutHelper<T extends ViewProvider> extends FrameLayout {
- /** @hide */
- final public T mProvider;
-
- /** @hide */
- public FrameLayoutHelper(ProviderCreator<T> creator,
- Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
-
- mProvider = creator.createProvider(this, new SuperProvider());
- }
-
- /** @hide */
- // TODO @SystemApi
- public T getProvider() {
- return mProvider;
- }
-
- @Override
- public CharSequence getAccessibilityClassName() {
- return mProvider.getAccessibilityClassName_impl();
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- return mProvider.onTouchEvent_impl(ev);
- }
-
- @Override
- public boolean onTrackballEvent(MotionEvent ev) {
- return mProvider.onTrackballEvent_impl(ev);
- }
-
- @Override
- public void onFinishInflate() {
- mProvider.onFinishInflate_impl();
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- mProvider.setEnabled_impl(enabled);
- }
-
- @Override
- protected void onAttachedToWindow() {
- mProvider.onAttachedToWindow_impl();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- mProvider.onDetachedFromWindow_impl();
- }
-
- /** @hide */
- public class SuperProvider implements ViewProvider {
- @Override
- public CharSequence getAccessibilityClassName_impl() {
- return FrameLayoutHelper.super.getAccessibilityClassName();
- }
-
- @Override
- public boolean onTouchEvent_impl(MotionEvent ev) {
- return FrameLayoutHelper.super.onTouchEvent(ev);
- }
-
- @Override
- public boolean onTrackballEvent_impl(MotionEvent ev) {
- return FrameLayoutHelper.super.onTrackballEvent(ev);
- }
-
- @Override
- public void onFinishInflate_impl() {
- FrameLayoutHelper.super.onFinishInflate();
- }
-
- @Override
- public void setEnabled_impl(boolean enabled) {
- FrameLayoutHelper.super.setEnabled(enabled);
- }
-
- @Override
- public void onAttachedToWindow_impl() {
- FrameLayoutHelper.super.onAttachedToWindow();
- }
-
- @Override
- public void onDetachedFromWindow_impl() {
- FrameLayoutHelper.super.onDetachedFromWindow();
- }
- }
-
- /** @hide */
- @FunctionalInterface
- public interface ProviderCreator<U extends ViewProvider> {
- U createProvider(FrameLayoutHelper<U> instance, ViewProvider superProvider);
- }
-}
diff --git a/media/java/android/media/update/MediaControlView2Provider.java b/media/java/android/media/update/MediaControlView2Provider.java
index 95fe363..e155e5f 100644
--- a/media/java/android/media/update/MediaControlView2Provider.java
+++ b/media/java/android/media/update/MediaControlView2Provider.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.media.session.MediaController;
+import android.util.AttributeSet;
import android.view.View;
/**
@@ -34,12 +35,12 @@
* @hide
*/
// TODO @SystemApi
-public interface MediaControlView2Provider extends ViewProvider {
+public interface MediaControlView2Provider extends ViewGroupProvider {
+ void initialize(AttributeSet attrs, int defStyleAttr, int defStyleRes);
+
void setController_impl(MediaController controller);
- boolean isShowing_impl();
void setButtonVisibility_impl(int button, int visibility);
void requestPlayButtonFocus_impl();
- void onVisibilityAggregated_impl(boolean isVisible);
void setTimeout_impl(long timeout);
long getTimeout_impl();
}
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index 9abf34a..41162e0 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -30,7 +30,7 @@
import android.media.MediaSession2.PlaylistParams;
import android.media.MediaSession2.SessionCallback;
import android.media.SessionToken2;
-import android.media.VolumeProvider;
+import android.media.VolumeProvider2;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -44,7 +44,7 @@
public interface MediaSession2Provider extends TransportControlProvider {
void close_impl();
void setPlayer_impl(MediaPlayerInterface player);
- void setPlayer_impl(MediaPlayerInterface player, VolumeProvider volumeProvider);
+ void setPlayer_impl(MediaPlayerInterface player, VolumeProvider2 volumeProvider);
MediaPlayerInterface getPlayer_impl();
SessionToken2 getToken_impl();
List<ControllerInfo> getConnectedControllers_impl();
@@ -116,7 +116,7 @@
}
interface BuilderBaseProvider<T extends MediaSession2, C extends SessionCallback> {
- void setVolumeProvider_impl(VolumeProvider volumeProvider);
+ void setVolumeProvider_impl(VolumeProvider2 volumeProvider);
void setRatingType_impl(int type);
void setSessionActivity_impl(PendingIntent pi);
void setId_impl(String id);
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 862a402..57f04cc 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -67,10 +67,11 @@
* @hide
*/
public interface StaticProvider {
- MediaControlView2Provider createMediaControlView2(
- MediaControlView2 instance, ViewProvider superProvider);
- VideoView2Provider createVideoView2(
- VideoView2 instance, ViewProvider superProvider,
+ MediaControlView2Provider createMediaControlView2(MediaControlView2 instance,
+ ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
+ @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
+ VideoView2Provider createVideoView2(VideoView2 instance,
+ ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
CommandProvider createMediaSession2Command(MediaSession2.Command instance,
diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java
index 10f03d2..7251180 100644
--- a/media/java/android/media/update/VideoView2Provider.java
+++ b/media/java/android/media/update/VideoView2Provider.java
@@ -16,12 +16,14 @@
package android.media.update;
+import android.annotation.SystemApi;
import android.media.AudioAttributes;
import android.media.MediaPlayerInterface;
import android.media.session.MediaController;
import android.media.session.PlaybackState;
import android.media.session.MediaSession;
import android.net.Uri;
+import android.util.AttributeSet;
import android.widget.MediaControlView2;
import android.widget.VideoView2;
@@ -43,7 +45,9 @@
* @hide
*/
// TODO @SystemApi
-public interface VideoView2Provider extends ViewProvider {
+public interface VideoView2Provider extends ViewGroupProvider {
+ void initialize(AttributeSet attrs, int defStyleAttr, int defStyleRes);
+
void setMediaControlView2_impl(MediaControlView2 mediaControlView);
MediaController getMediaController_impl();
MediaControlView2 getMediaControlView2_impl();
@@ -52,6 +56,9 @@
void setSpeed_impl(float speed);
void setAudioFocusRequest_impl(int focusGain);
void setAudioAttributes_impl(AudioAttributes attributes);
+ /**
+ * @hide
+ */
void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerInterface player);
// TODO: remove setRouteAttributes_impl with MediaSession.Callback once MediaSession2 is ready.
void setRouteAttributes_impl(List<String> routeCategories, MediaSession.Callback sessionPlayer);
diff --git a/media/java/android/media/update/ViewGroupHelper.java b/media/java/android/media/update/ViewGroupHelper.java
new file mode 100644
index 0000000..2c4f9b9
--- /dev/null
+++ b/media/java/android/media/update/ViewGroupHelper.java
@@ -0,0 +1,354 @@
+/*
+ * 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.media.update;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Helper class for connecting the public API to an updatable implementation.
+ *
+ * @see ViewGroupProvider
+ *
+ * @hide
+ */
+public abstract class ViewGroupHelper<T extends ViewGroupProvider> extends ViewGroup {
+ /** @hide */
+ final public T mProvider;
+
+ /** @hide */
+ public ViewGroupHelper(ProviderCreator<T> creator,
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ mProvider = creator.createProvider(this, new SuperProvider(),
+ new PrivateProvider());
+ }
+
+ /** @hide */
+ // TODO @SystemApi
+ public T getProvider() {
+ return mProvider;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ mProvider.onAttachedToWindow_impl();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ mProvider.onDetachedFromWindow_impl();
+ }
+
+ @Override
+ public CharSequence getAccessibilityClassName() {
+ return mProvider.getAccessibilityClassName_impl();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return mProvider.onTouchEvent_impl(ev);
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent ev) {
+ return mProvider.onTrackballEvent_impl(ev);
+ }
+
+ @Override
+ public void onFinishInflate() {
+ mProvider.onFinishInflate_impl();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ mProvider.setEnabled_impl(enabled);
+ }
+
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ mProvider.onVisibilityAggregated_impl(isVisible);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mProvider.onLayout_impl(changed, left, top, right, bottom);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mProvider.onMeasure_impl(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ protected int getSuggestedMinimumWidth() {
+ return mProvider.getSuggestedMinimumWidth_impl();
+ }
+
+ @Override
+ protected int getSuggestedMinimumHeight() {
+ return mProvider.getSuggestedMinimumHeight_impl();
+ }
+
+ // setMeasuredDimension is final
+
+ @Override
+ protected boolean checkLayoutParams(LayoutParams p) {
+ return mProvider.checkLayoutParams_impl(p);
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ return mProvider.generateDefaultLayoutParams_impl();
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return mProvider.generateLayoutParams_impl(attrs);
+ }
+
+ @Override
+ protected LayoutParams generateLayoutParams(LayoutParams lp) {
+ return mProvider.generateLayoutParams_impl(lp);
+ }
+
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return mProvider.shouldDelayChildPressedState_impl();
+ }
+
+ @Override
+ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed) {
+ mProvider.measureChildWithMargins_impl(child,
+ parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+ }
+
+ /** @hide */
+ public class SuperProvider implements ViewGroupProvider {
+ @Override
+ public CharSequence getAccessibilityClassName_impl() {
+ return ViewGroupHelper.super.getAccessibilityClassName();
+ }
+
+ @Override
+ public boolean onTouchEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.super.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTrackballEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.super.onTrackballEvent(ev);
+ }
+
+ @Override
+ public void onFinishInflate_impl() {
+ ViewGroupHelper.super.onFinishInflate();
+ }
+
+ @Override
+ public void setEnabled_impl(boolean enabled) {
+ ViewGroupHelper.super.setEnabled(enabled);
+ }
+
+ @Override
+ public void onAttachedToWindow_impl() {
+ ViewGroupHelper.super.onAttachedToWindow();
+ }
+
+ @Override
+ public void onDetachedFromWindow_impl() {
+ ViewGroupHelper.super.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onVisibilityAggregated_impl(boolean isVisible) {
+ ViewGroupHelper.super.onVisibilityAggregated(isVisible);
+ }
+
+ @Override
+ public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
+ // abstract method; no super
+ }
+
+ @Override
+ public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
+ ViewGroupHelper.super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public int getSuggestedMinimumWidth_impl() {
+ return ViewGroupHelper.super.getSuggestedMinimumWidth();
+ }
+
+ @Override
+ public int getSuggestedMinimumHeight_impl() {
+ return ViewGroupHelper.super.getSuggestedMinimumHeight();
+ }
+
+ @Override
+ public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) {
+ ViewGroupHelper.super.setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ @Override
+ public boolean checkLayoutParams_impl(LayoutParams p) {
+ return ViewGroupHelper.super.checkLayoutParams(p);
+ }
+
+ @Override
+ public LayoutParams generateDefaultLayoutParams_impl() {
+ return ViewGroupHelper.super.generateDefaultLayoutParams();
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
+ return ViewGroupHelper.super.generateLayoutParams(attrs);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams_impl(LayoutParams lp) {
+ return ViewGroupHelper.super.generateLayoutParams(lp);
+ }
+
+ @Override
+ public boolean shouldDelayChildPressedState_impl() {
+ return ViewGroupHelper.super.shouldDelayChildPressedState();
+ }
+
+ @Override
+ public void measureChildWithMargins_impl(View child,
+ int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed) {
+ ViewGroupHelper.super.measureChildWithMargins(child,
+ parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+ }
+ }
+
+ /** @hide */
+ public class PrivateProvider implements ViewGroupProvider {
+ @Override
+ public CharSequence getAccessibilityClassName_impl() {
+ return ViewGroupHelper.this.getAccessibilityClassName();
+ }
+
+ @Override
+ public boolean onTouchEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.this.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTrackballEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.this.onTrackballEvent(ev);
+ }
+
+ @Override
+ public void onFinishInflate_impl() {
+ ViewGroupHelper.this.onFinishInflate();
+ }
+
+ @Override
+ public void setEnabled_impl(boolean enabled) {
+ ViewGroupHelper.this.setEnabled(enabled);
+ }
+
+ @Override
+ public void onAttachedToWindow_impl() {
+ ViewGroupHelper.this.onAttachedToWindow();
+ }
+
+ @Override
+ public void onDetachedFromWindow_impl() {
+ ViewGroupHelper.this.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onVisibilityAggregated_impl(boolean isVisible) {
+ ViewGroupHelper.this.onVisibilityAggregated(isVisible);
+ }
+
+ @Override
+ public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
+ ViewGroupHelper.this.onLayout(changed, left, top, right, bottom);
+ }
+
+ @Override
+ public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
+ ViewGroupHelper.this.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public int getSuggestedMinimumWidth_impl() {
+ return ViewGroupHelper.this.getSuggestedMinimumWidth();
+ }
+
+ @Override
+ public int getSuggestedMinimumHeight_impl() {
+ return ViewGroupHelper.this.getSuggestedMinimumHeight();
+ }
+
+ @Override
+ public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) {
+ ViewGroupHelper.this.setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ @Override
+ public boolean checkLayoutParams_impl(LayoutParams p) {
+ return ViewGroupHelper.this.checkLayoutParams(p);
+ }
+
+ @Override
+ public LayoutParams generateDefaultLayoutParams_impl() {
+ return ViewGroupHelper.this.generateDefaultLayoutParams();
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
+ return ViewGroupHelper.this.generateLayoutParams(attrs);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams_impl(LayoutParams lp) {
+ return ViewGroupHelper.this.generateLayoutParams(lp);
+ }
+
+ @Override
+ public boolean shouldDelayChildPressedState_impl() {
+ return ViewGroupHelper.this.shouldDelayChildPressedState();
+ }
+
+ @Override
+ public void measureChildWithMargins_impl(View child,
+ int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed) {
+ ViewGroupHelper.this.measureChildWithMargins(child,
+ parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+ }
+ }
+
+ /** @hide */
+ @FunctionalInterface
+ public interface ProviderCreator<T extends ViewGroupProvider> {
+ T createProvider(ViewGroupHelper<T> instance, ViewGroupProvider superProvider,
+ ViewGroupProvider privateProvider);
+ }
+}
diff --git a/media/java/android/media/update/ViewGroupProvider.java b/media/java/android/media/update/ViewGroupProvider.java
new file mode 100644
index 0000000..5f12529
--- /dev/null
+++ b/media/java/android/media/update/ViewGroupProvider.java
@@ -0,0 +1,66 @@
+/*
+ * 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.media.update;
+
+import android.annotation.SystemApi;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+/**
+ * Interface for connecting the public API to an updatable implementation.
+ *
+ * Each instance object is connected to one corresponding updatable object which implements the
+ * runtime behavior of that class. There should a corresponding provider method for all public
+ * methods.
+ *
+ * All methods behave as per their namesake in the public API.
+ *
+ * @see android.view.View
+ *
+ * @hide
+ */
+// TODO @SystemApi
+public interface ViewGroupProvider {
+ // View methods
+ void onAttachedToWindow_impl();
+ void onDetachedFromWindow_impl();
+ CharSequence getAccessibilityClassName_impl();
+ boolean onTouchEvent_impl(MotionEvent ev);
+ boolean onTrackballEvent_impl(MotionEvent ev);
+ void onFinishInflate_impl();
+ void setEnabled_impl(boolean enabled);
+ void onVisibilityAggregated_impl(boolean isVisible);
+ void onLayout_impl(boolean changed, int left, int top, int right, int bottom);
+ void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec);
+ int getSuggestedMinimumWidth_impl();
+ int getSuggestedMinimumHeight_impl();
+ void setMeasuredDimension_impl(int measuredWidth, int measuredHeight);
+
+ // ViewGroup methods
+ boolean checkLayoutParams_impl(LayoutParams p);
+ LayoutParams generateDefaultLayoutParams_impl();
+ LayoutParams generateLayoutParams_impl(AttributeSet attrs);
+ LayoutParams generateLayoutParams_impl(LayoutParams lp);
+ boolean shouldDelayChildPressedState_impl();
+ void measureChildWithMargins_impl(View child, int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed);
+
+ // ViewManager methods
+ // ViewParent methods
+}
diff --git a/media/java/android/media/update/ViewProvider.java b/media/java/android/media/update/ViewProvider.java
deleted file mode 100644
index 0dd8f38..0000000
--- a/media/java/android/media/update/ViewProvider.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-package android.media.update;
-
-import android.annotation.SystemApi;
-import android.view.MotionEvent;
-
-/**
- * Interface for connecting the public API to an updatable implementation.
- *
- * Each instance object is connected to one corresponding updatable object which implements the
- * runtime behavior of that class. There should a corresponding provider method for all public
- * methods.
- *
- * All methods behave as per their namesake in the public API.
- *
- * @see android.view.View
- *
- * @hide
- */
-// TODO @SystemApi
-public interface ViewProvider {
- // TODO Add more (all?) methods from View
- void onAttachedToWindow_impl();
- void onDetachedFromWindow_impl();
- CharSequence getAccessibilityClassName_impl();
- boolean onTouchEvent_impl(MotionEvent ev);
- boolean onTrackballEvent_impl(MotionEvent ev);
- void onFinishInflate_impl();
- void setEnabled_impl(boolean enabled);
-}
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 90ee8a6..27eaed0 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -19,12 +19,16 @@
#define LOG_TAG "MediaPlayer2-JNI"
#include "utils/Log.h"
+#include <sys/stat.h>
+
#include <media/mediaplayer2.h>
#include <media/AudioResamplerPublic.h>
+#include <media/DataSourceDesc.h>
#include <media/MediaHTTPService.h>
#include <media/MediaPlayer2Interface.h>
#include <media/MediaAnalyticsItem.h>
#include <media/NdkWrapper.h>
+#include <media/stagefright/Utils.h>
#include <media/stagefright/foundation/ByteUtils.h> // for FOURCC definition
#include <stdio.h>
#include <assert.h>
@@ -234,7 +238,8 @@
// event to the client application; otherwise, if exception is not NULL and
// opStatus is not OK, this method throws the given exception to the client
// application.
-static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
+static void process_media_player_call(
+ JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
if (exception == NULL) { // Don't throw exception. Instead, send an event.
if (opStatus != (status_t) OK) {
@@ -268,7 +273,7 @@
jobjectArray keys, jobjectArray values) {
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
+ if (mp == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
@@ -282,16 +287,25 @@
if (tmp == NULL) { // Out of memory
return;
}
- ALOGV("setDataSource: path %s", tmp);
+ ALOGV("setDataSourceAndHeaders: path %s", tmp);
- String8 pathStr(tmp);
+ if (strncmp(tmp, "content://", 10) == 0) {
+ ALOGE("setDataSourceAndHeaders: content scheme is not supported in native code");
+ jniThrowException(env, "java/io/IOException",
+ "content scheme is not supported in native code");
+ return;
+ }
+
+ sp<DataSourceDesc> dsd = new DataSourceDesc();
+ dsd->mType = DataSourceDesc::TYPE_URL;
+ dsd->mUrl = tmp;
+
env->ReleaseStringUTFChars(path, tmp);
tmp = NULL;
// We build a KeyedVector out of the key and val arrays
- KeyedVector<String8, String8> headersVector;
if (!ConvertKeyValueArraysToKeyedVector(
- env, keys, values, &headersVector)) {
+ env, keys, values, &dsd->mHeaders)) {
return;
}
@@ -299,20 +313,16 @@
if (httpServiceObj != NULL) {
httpService = new JMedia2HTTPService(env, httpServiceObj);
}
-
- status_t opStatus =
- mp->setDataSource(
- httpService,
- pathStr,
- headersVector.size() > 0? &headersVector : NULL);
+ dsd->mHttpService = httpService;
process_media_player_call(
- env, thiz, opStatus, "java/io/IOException",
- "setDataSource failed." );
+ env, thiz, mp->setDataSource(dsd), "java/io/IOException",
+ "setDataSourceAndHeaders failed." );
}
static void
-android_media_MediaPlayer2_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
+android_media_MediaPlayer2_setDataSourceFD(
+ JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -325,12 +335,46 @@
return;
}
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- ALOGV("setDataSourceFD: fd %d", fd);
- process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
+ ALOGV("setDataSourceFD: fd=%d (%s), offset=%lld, length=%lld",
+ fd, nameForFd(fd).c_str(), (long long)offset, (long long)length);
+
+ struct stat sb;
+ int ret = fstat(fd, &sb);
+ if (ret != 0) {
+ ALOGE("setDataSourceFD: fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
+ jniThrowException(env, "java/io/IOException", "setDataSourceFD failed fstat");
+ return;
+ }
+
+ ALOGV("st_dev = %llu", static_cast<unsigned long long>(sb.st_dev));
+ ALOGV("st_mode = %u", sb.st_mode);
+ ALOGV("st_uid = %lu", static_cast<unsigned long>(sb.st_uid));
+ ALOGV("st_gid = %lu", static_cast<unsigned long>(sb.st_gid));
+ ALOGV("st_size = %llu", static_cast<unsigned long long>(sb.st_size));
+
+ if (offset >= sb.st_size) {
+ ALOGE("setDataSourceFD: offset is out of range");
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "setDataSourceFD failed, offset is out of range.");
+ return;
+ }
+ if (offset + length > sb.st_size) {
+ length = sb.st_size - offset;
+ ALOGV("setDataSourceFD: adjusted length = %lld", (long long)length);
+ }
+
+ sp<DataSourceDesc> dsd = new DataSourceDesc();
+ dsd->mType = DataSourceDesc::TYPE_FD;
+ dsd->mFD = fd;
+ dsd->mFDOffset = offset;
+ dsd->mFDLength = length;
+ process_media_player_call(env, thiz, mp->setDataSource(dsd),
+ "java/io/IOException", "setDataSourceFD failed." );
}
static void
-android_media_MediaPlayer2_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject dataSource)
+android_media_MediaPlayer2_setDataSourceCallback(
+ JNIEnv *env, jobject thiz, jobject dataSource)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -343,7 +387,11 @@
return;
}
sp<DataSource> callbackDataSource = new JMedia2DataSource(env, dataSource);
- process_media_player_call(env, thiz, mp->setDataSource(callbackDataSource), "java/lang/RuntimeException", "setDataSourceCallback failed." );
+ sp<DataSourceDesc> dsd = new DataSourceDesc();
+ dsd->mType = DataSourceDesc::TYPE_CALLBACK;
+ dsd->mCallbackSource = callbackDataSource;
+ process_media_player_call(env, thiz, mp->setDataSource(dsd),
+ "java/lang/RuntimeException", "setDataSourceCallback failed." );
}
static sp<ANativeWindowWrapper>
@@ -1099,45 +1147,6 @@
process_media_player_call( env, thiz, mp->attachAuxEffect(effectId), NULL, NULL );
}
-static jint
-android_media_MediaPlayer2_setRetransmitEndpoint(JNIEnv *env, jobject thiz,
- jstring addrString, jint port) {
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return INVALID_OPERATION;
- }
-
- const char *cAddrString = NULL;
-
- if (NULL != addrString) {
- cAddrString = env->GetStringUTFChars(addrString, NULL);
- if (cAddrString == NULL) { // Out of memory
- return NO_MEMORY;
- }
- }
- ALOGV("setRetransmitEndpoint: %s:%d",
- cAddrString ? cAddrString : "(null)", port);
-
- status_t ret;
- if (cAddrString && (port > 0xFFFF)) {
- ret = BAD_VALUE;
- } else {
- ret = mp->setRetransmitEndpoint(cAddrString,
- static_cast<uint16_t>(port));
- }
-
- if (NULL != addrString) {
- env->ReleaseStringUTFChars(addrString, cAddrString);
- }
-
- if (ret == INVALID_OPERATION ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- }
-
- return (jint) ret;
-}
-
static void
android_media_MediaPlayer2_setNextMediaPlayer(JNIEnv *env, jobject thiz, jobject java_player)
{
@@ -1418,7 +1427,6 @@
{"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer2_set_audio_session_id},
{"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer2_setAuxEffectSendLevel},
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer2_attachAuxEffect},
- {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer2_setRetransmitEndpoint},
{"setNextMediaPlayer", "(Landroid/media/MediaPlayer2;)V", (void *)android_media_MediaPlayer2_setNextMediaPlayer},
// Modular DRM
{ "_prepareDrm", "([B[B)V", (void *)android_media_MediaPlayer2_prepareDrm },
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 8b01aef..9f165bc 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -30,6 +30,7 @@
import android.content.res.ObbScanner;
import android.os.Binder;
import android.os.Environment.UserEnvironment;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -43,7 +44,6 @@
import com.android.internal.util.ArrayUtils;
import libcore.io.IoUtils;
-import libcore.io.Streams;
import java.io.File;
import java.io.FileInputStream;
@@ -260,7 +260,7 @@
in = new FileInputStream(sourcePath);
out = new ParcelFileDescriptor.AutoCloseOutputStream(
target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
- Streams.copy(in, out);
+ FileUtils.copy(in, out);
} finally {
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(in);
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 6fe8975..9a66b07 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -94,7 +94,7 @@
infile = mFile.openRead();
readXml(infile);
} catch (FileNotFoundException e) {
- // No data yet
+ Log.d(TAG, "File doesn't exist or isn't readable yet");
} catch (IOException e) {
Log.e(TAG, "Unable to read channel impressions", e);
} catch (NumberFormatException | XmlPullParserException e) {
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index 7c35b48..db48f61 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -325,7 +325,8 @@
int dismiss2 = 777;
String key2 = mAssistant.getKey("pkg2", 2, "channel2");
- String xml = "<assistant version=\"1\">\n"
+ String xml = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<assistant version=\"1\">\n"
+ "<impression-set key=\"" + key1 + "\" "
+ "dismisses=\"" + dismiss1 + "\" views=\"" + views1
+ "\" streak=\"" + streak1 + "\"/>\n"
@@ -377,7 +378,6 @@
mAssistant.insertImpressions(key2, ci2);
mAssistant.insertImpressions(key3, ci3);
-
XmlSerializer serializer = new FastXmlSerializer();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
index b5ef1d7..61fe906 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
@@ -78,7 +78,25 @@
android:id="@+id/screen_pinning_home_group"
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
- android:layout_weight="0" >
+ android:layout_weight="0"
+ android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg_light"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scaleType="matrix"
+ android:src="@drawable/screen_pinning_light_bg_circ" />
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scaleType="matrix"
+ android:paddingLeft="@dimen/screen_pinning_request_inner_padding"
+ android:paddingTop="@dimen/screen_pinning_request_inner_padding"
+ android:paddingBottom="@dimen/screen_pinning_request_inner_padding"
+ android:src="@drawable/screen_pinning_bg_circ" />
<ImageView
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
index f3a6d44..d1ca2ce 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
@@ -80,7 +80,27 @@
android:id="@+id/screen_pinning_home_group"
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
- android:layout_weight="0" >
+ android:layout_weight="0"
+ android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent" >
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg_light"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scaleType="matrix"
+ android:layout_marginLeft="@dimen/screen_pinning_request_seascape_padding_negative"
+ android:src="@drawable/screen_pinning_light_bg_circ" />
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scaleType="matrix"
+ android:layout_marginLeft="@dimen/screen_pinning_request_seascape_button_offset"
+ android:paddingRight="@dimen/screen_pinning_request_inner_padding"
+ android:paddingTop="@dimen/screen_pinning_request_inner_padding"
+ android:paddingBottom="@dimen/screen_pinning_request_inner_padding"
+ android:src="@drawable/screen_pinning_bg_circ" />
<ImageView
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index a9e5adf..cdd417f 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -73,15 +73,18 @@
android:id="@+id/volume_row_slider_frame"
android:padding="0dp"
android:layout_width="@dimen/volume_dialog_panel_width"
+ android:layoutDirection="rtl"
android:layout_height="150dp">
<SeekBar
android:id="@+id/volume_row_slider"
+ android:clickable="true"
android:padding="0dp"
android:layout_margin="0dp"
android:layout_width="150dp"
android:layout_height="@dimen/volume_dialog_panel_width"
+ android:layoutDirection="rtl"
android:layout_gravity="center"
- android:rotation="270" />
+ android:rotation="90" />
</FrameLayout>
<com.android.keyguard.AlphaOptimizedImageButton
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 3db79d7..16069041 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -902,4 +902,11 @@
<dimen name="config_batteryLevelTextSizeEnd" format="float">32.0</dimen>
<!-- Wireless Charging battery level text animation duration -->
<integer name="config_batteryLevelTextAnimationDuration">400</integer>
+
+ <!-- Wired charging on AOD, text animation duration -->
+ <integer name="wired_charging_aod_text_animation_duration_down">500</integer>
+ <!-- Wired charging on AOD, text animation duration -->
+ <integer name="wired_charging_aod_text_animation_duration_up">300</integer>
+ <!-- Wired charging on AOD, text animation distance -->
+ <integer name="wired_charging_aod_text_animation_distance">-30</integer>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index f9e1069..90e3b1e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -16,6 +16,8 @@
package com.android.systemui.shared.system;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -31,6 +33,7 @@
import android.app.AppGlobals;
import android.app.IAssistDataReceiver;
import android.app.WindowConfiguration.ActivityType;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -47,6 +50,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.IconDrawableFactory;
import android.util.Log;
import android.view.IRecentsAnimationController;
@@ -436,4 +440,23 @@
Log.w(TAG, "Failed to cancel window transition for task=" + taskId, e);
}
}
+
+ /**
+ * @return whether there is currently a locked task (ie. in screen pinning).
+ */
+ public boolean isLockToAppActive() {
+ try {
+ return ActivityManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * @return whether screen pinning is enabled.
+ */
+ public boolean isLockToAppEnabled() {
+ final ContentResolver cr = AppGlobals.getInitialApplication().getContentResolver();
+ return Settings.System.getInt(cr, Settings.System.LOCK_TO_APP_ENABLED, 0) != 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index b8cfa3e..aeef496 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -18,6 +18,7 @@
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
+import android.view.animation.BounceInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
@@ -43,6 +44,7 @@
public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
public static final Interpolator PANEL_CLOSE_ACCELERATED
= new PathInterpolator(0.3f, 0, 0.5f, 1);
+ public static final Interpolator BOUNCE = new BounceInterpolator();
/**
* Interpolator to be used when animating a move based on a click. Pair with enough duration.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 653e500..eedc50f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -707,6 +707,9 @@
&& !mLockPatternUtils.isLockScreenDisabled(
KeyguardUpdateMonitor.getCurrentUser()),
mSecondaryDisplayShowing, true /* forceCallbacks */);
+ } else {
+ // The system's keyguard is disabled or missing.
+ setShowingLocked(false, mSecondaryDisplayShowing, true);
}
mStatusBarKeyguardViewManager =
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index e661fa7..ea2a432 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -142,8 +142,12 @@
final int lowPowerModeTriggerLevel = Settings.Global.getInt(resolver,
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
- // NOTE: Keep the logic in sync with BatteryService.
- // TODO: Propagate this value from BatteryService to system UI, really.
+ // Note LOW_POWER_MODE_TRIGGER_LEVEL can take any value between 0 and 100, but
+ // for the UI purposes, let's cap it at 15% -- i.e. even if the trigger level is higher
+ // like 50%, let's not show the "low battery" notification until it hits
+ // config_lowBatteryWarningLevel, which is 15% by default.
+ // LOW_POWER_MODE_TRIGGER_LEVEL is still used in other places as-is. For example, if it's
+ // 50, then battery saver kicks in when the battery level hits 50%.
int warnLevel = Math.min(defWarnLevel, lowPowerModeTriggerLevel);
if (warnLevel == 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 6b0d592..6ccb817 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -36,7 +36,6 @@
public class QSContainerImpl extends FrameLayout {
private final Point mSizePoint = new Point();
- private final Path mClipPath = new Path();
private int mHeightOverride = -1;
protected View mQSPanel;
@@ -46,7 +45,6 @@
private QSCustomizer mQSCustomizer;
private View mQSFooter;
private View mBackground;
- private float mRadius;
private int mSideMargins;
public QSContainerImpl(Context context, AttributeSet attrs) {
@@ -62,8 +60,6 @@
mQSCustomizer = findViewById(R.id.qs_customize);
mQSFooter = findViewById(R.id.qs_footer);
mBackground = findViewById(R.id.quick_settings_background);
- mRadius = getResources().getDimensionPixelSize(
- Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
setClickable(true);
@@ -115,18 +111,6 @@
updateExpansion();
}
- @Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- boolean ret;
- canvas.save();
- if (child != mQSCustomizer) {
- canvas.clipPath(mClipPath);
- }
- ret = super.drawChild(canvas, child, drawingTime);
- canvas.restore();
- return ret;
- }
-
/**
* Overrides the height of this view (post-layout), so that the content is clipped to that
* height and the background is set to that height.
@@ -146,10 +130,6 @@
mQSFooter.setTranslationY(height - mQSFooter.getHeight());
mBackground.setTop(mQSPanel.getTop());
mBackground.setBottom(height);
-
- ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius,
- mRadius,
- mClipPath);
}
protected int calculateContainerHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index aaf6ef5..6205e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -19,8 +19,6 @@
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
-import android.app.AlarmManager;
-import android.app.AlarmManager.AlarmClockInfo;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -30,11 +28,9 @@
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.net.Uri;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Global;
-import android.service.notification.ScheduleCalendar;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ZenRule;
import android.service.quicksettings.Tile;
@@ -56,7 +52,6 @@
import com.android.systemui.SysUIToast;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
-import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -197,7 +192,8 @@
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.slash.isSlashed = !state.value;
state.label = getTileLabel();
- state.secondaryLabel = getSecondaryLabel(zen != Global.ZEN_MODE_OFF);
+ state.secondaryLabel = ZenModeConfig.getDescription(mContext,zen != Global.ZEN_MODE_OFF,
+ mController.getConfig());
state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
switch (zen) {
@@ -226,102 +222,6 @@
state.expandedAccessibilityClassName = Switch.class.getName();
}
- /**
- * Returns the secondary label to use for the given instance of do not disturb.
- * - If turned on manually and end time is known, returns end time.
- * - If turned on by an automatic rule, returns the automatic rule name.
- * - If on due to an app, returns the app name.
- * - If there's a combination of rules/apps that trigger, then shows the one that will
- * last the longest if applicable.
- * @return null if do not disturb is off.
- */
- private String getSecondaryLabel(boolean zenOn) {
- if (!zenOn) {
- return null;
- }
-
- ZenModeConfig config = mController.getConfig();
- String secondaryText = "";
- long latestEndTime = -1;
-
- // DND turned on by manual rule
- if (config.manualRule != null) {
- final Uri id = config.manualRule.conditionId;
- if (config.manualRule.enabler != null) {
- // app triggered manual rule
- String appName = ZenModeConfig.getOwnerCaption(mContext, config.manualRule.enabler);
- if (!appName.isEmpty()) {
- secondaryText = appName;
- }
- } else {
- if (id == null) {
- // Do not disturb manually triggered to remain on forever until turned off
- // No subtext
- return null;
- } else {
- latestEndTime = ZenModeConfig.tryParseCountdownConditionId(id);
- if (latestEndTime > 0) {
- final CharSequence formattedTime = ZenModeConfig.getFormattedTime(mContext,
- latestEndTime, ZenModeConfig.isToday(latestEndTime),
- mContext.getUserId());
- secondaryText = mContext.getString(R.string.qs_dnd_until, formattedTime);
- }
- }
- }
- }
-
- // DND turned on by an automatic rule
- for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) {
- if (automaticRule.isAutomaticActive()) {
- if (ZenModeConfig.isValidEventConditionId(automaticRule.conditionId) ||
- ZenModeConfig.isValidScheduleConditionId(automaticRule.conditionId)) {
- // set text if automatic rule end time is the latest active rule end time
- long endTime = parseAutomaticRuleEndTime(automaticRule.conditionId);
- if (endTime > latestEndTime) {
- latestEndTime = endTime;
- secondaryText = automaticRule.name;
- }
- } else {
- // set text if 3rd party rule
- return automaticRule.name;
- }
- }
- }
-
- return !secondaryText.equals("") ? secondaryText : null;
- }
-
- private long parseAutomaticRuleEndTime(Uri id) {
- if (ZenModeConfig.isValidEventConditionId(id)) {
- // cannot look up end times for events
- return Long.MAX_VALUE;
- }
-
- if (ZenModeConfig.isValidScheduleConditionId(id)) {
- ScheduleCalendar schedule = ZenModeConfig.toScheduleCalendar(id);
- long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis());
-
- // check if automatic rule will end on next alarm
- if (schedule.exitAtAlarm()) {
- long nextAlarm = getNextAlarm(mContext);
- schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm);
- if (schedule.shouldExitForAlarm(endTimeMs)) {
- return nextAlarm;
- }
- }
-
- return endTimeMs;
- }
-
- return -1;
- }
-
- private long getNextAlarm(Context context) {
- final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- final AlarmClockInfo info = alarms.getNextAlarmClock(mContext.getUserId());
- return info != null ? info.getTriggerTime() : 0;
- }
-
@Override
public int getMetricsCategory() {
return MetricsEvent.QS_DND;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 8bdbf28..d7f2a26 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -102,7 +102,7 @@
state.value = locationEnabled;
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_SHARE_LOCATION);
if (state.disabledByPolicy == false) {
- checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_LOCATION_MODE);
+ checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_LOCATION);
}
state.icon = mIcon;
state.slash.isSlashed = !state.value;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 1da4deb..409c753 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -415,7 +415,7 @@
final int activityType = runningTask != null
? runningTask.configuration.windowConfiguration.getActivityType()
: ACTIVITY_TYPE_UNDEFINED;
- boolean screenPinningActive = sSystemServicesProxy.isScreenPinningActive();
+ boolean screenPinningActive = ActivityManagerWrapper.getInstance().isLockToAppActive();
boolean isRunningTaskInHomeOrRecentsStack =
activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index ee1b091..3f6f30b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -386,8 +386,7 @@
public void toggleRecents(int growTarget) {
// Skip preloading if the task is locked
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.isScreenPinningActive()) {
+ if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
return;
}
@@ -409,8 +408,8 @@
MutableBoolean isHomeStackVisible = new MutableBoolean(true);
long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
+ SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isRecentsActivityVisible(isHomeStackVisible)) {
- RecentsDebugFlags debugFlags = Recents.getDebugFlags();
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
if (!launchState.launchedWithAltTab) {
@@ -466,8 +465,7 @@
public void preloadRecents() {
// Skip preloading if the task is locked
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.isScreenPinningActive()) {
+ if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
return;
}
@@ -481,6 +479,7 @@
// RecentsActivity) only if there is a task to animate to. Post this to ensure that we
// don't block the touch feedback on the nav bar button which triggers this.
mHandler.post(() -> {
+ SystemServicesProxy ssp = Recents.getSystemServices();
if (!ssp.isRecentsActivityVisible(null)) {
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 613d9fb..93fd34a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -379,29 +379,6 @@
}
/**
- * Returns a global setting.
- */
- public int getGlobalSetting(Context context, String setting) {
- ContentResolver cr = context.getContentResolver();
- return Settings.Global.getInt(cr, setting, 0);
- }
-
- /**
- * Returns a system setting.
- */
- public int getSystemSetting(Context context, String setting) {
- ContentResolver cr = context.getContentResolver();
- return Settings.System.getInt(cr, setting, 0);
- }
-
- /**
- * Returns a system property.
- */
- public String getSystemProperty(String key) {
- return SystemProperties.get(key);
- }
-
- /**
* Returns the smallest width/height.
*/
public int getDeviceSmallestWidth() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 5be2900..3cc3273 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -95,6 +95,7 @@
import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
import com.android.systemui.recents.views.grid.TaskViewFocusFrame;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -2187,8 +2188,7 @@
private void readSystemFlags() {
SystemServicesProxy ssp = Recents.getSystemServices();
mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
- mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
- Settings.System.LOCK_TO_APP_ENABLED) != 0;
+ mScreenPinningEnabled = ActivityManagerWrapper.getInstance().isLockToAppEnabled();
}
private void updateStackActionButtonVisibility() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index f5f62b85..11bdf6b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -789,7 +789,7 @@
break;
case MSG_SHOW_PINNING_TOAST_ENTER_EXIT:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).showPinningEnterExitToast(msg.arg1 != 0);
+ mCallbacks.get(i).showPinningEnterExitToast((Boolean) msg.obj);
}
break;
case MSG_SHOW_PINNING_TOAST_ESCAPE:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 0a12be4..b7a1500 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -44,6 +46,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.statusbar.phone.LockIcon;
@@ -192,7 +195,7 @@
if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
hideTransientIndication();
}
- updateIndication();
+ updateIndication(false);
} else if (!visible) {
// If we unlock and return to keyguard quickly, previous error should not be shown
hideTransientIndication();
@@ -204,7 +207,7 @@
*/
public void setRestingIndication(String restingIndication) {
mRestingIndication = restingIndication;
- updateIndication();
+ updateIndication(false);
}
/**
@@ -265,7 +268,8 @@
mWakeLock.setAcquired(true);
hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
}
- updateIndication();
+
+ updateIndication(false);
}
/**
@@ -275,11 +279,11 @@
if (mTransientIndication != null) {
mTransientIndication = null;
mHandler.removeMessages(MSG_HIDE_TRANSIENT);
- updateIndication();
+ updateIndication(false);
}
}
- protected final void updateIndication() {
+ protected final void updateIndication(boolean animate) {
if (TextUtils.isEmpty(mTransientIndication)) {
mWakeLock.setAcquired(false);
}
@@ -295,7 +299,35 @@
mTextView.switchIndication(mTransientIndication);
} else if (mPowerPluggedIn) {
String indication = computePowerIndication();
- mTextView.switchIndication(indication);
+ if (animate) {
+ int yTranslation = mContext.getResources().getInteger(
+ R.integer.wired_charging_aod_text_animation_distance);
+ int animateUpDuration = mContext.getResources().getInteger(
+ R.integer.wired_charging_aod_text_animation_duration_up);
+ int animateDownDuration = mContext.getResources().getInteger(
+ R.integer.wired_charging_aod_text_animation_duration_down);
+ mTextView.animate()
+ .translationYBy(yTranslation)
+ .setInterpolator(Interpolators.LINEAR)
+ .setDuration(animateUpDuration)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mTextView.switchIndication(indication);
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mTextView.animate()
+ .setDuration(animateDownDuration)
+ .setInterpolator(Interpolators.BOUNCE)
+ .translationYBy(-1 * yTranslation)
+ .setListener(null);
+ }
+ });
+ } else {
+ mTextView.switchIndication(indication);
+ }
+
} else {
String percentage = NumberFormat.getPercentInstance()
.format(mBatteryLevel / 100f);
@@ -390,7 +422,7 @@
public void onReceive(Context context, Intent intent) {
mHandler.post(() -> {
if (mVisible) {
- updateIndication();
+ updateIndication(false);
}
});
}
@@ -412,7 +444,7 @@
return;
}
mDozing = dozing;
- updateIndication();
+ updateIndication(false);
updateDisclosure();
}
@@ -445,7 +477,7 @@
mChargingWattage = status.maxChargingWattage;
mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
mBatteryLevel = status.level;
- updateIndication();
+ updateIndication(!wasPluggedIn && mPowerPluggedIn);
if (mDozing) {
if (!wasPluggedIn && mPowerPluggedIn) {
showTransientIndication(computePowerIndication());
@@ -551,7 +583,7 @@
@Override
public void onUserUnlocked() {
if (mVisible) {
- updateIndication();
+ updateIndication(false);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 242be71..1239a9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -591,11 +591,9 @@
ButtonDispatcher backButton = mNavigationBarView.getBackButton();
backButton.setLongClickable(true);
- backButton.setOnLongClickListener(this::onLongPressBackRecents);
ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
homeButton.setOnTouchListener(this::onHomeTouch);
- homeButton.setOnLongClickListener(this::onHomeLongClick);
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
@@ -605,6 +603,7 @@
ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);
+ updateScreenPinningGestures();
}
private boolean onHomeTouch(View v, MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index cd220a7..c37dd55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -300,7 +300,7 @@
}
}
}
- return mRecentsAnimationStarted || mGestureHelper.onInterceptTouchEvent(event);
+ return mGestureHelper.onInterceptTouchEvent(event) || mRecentsAnimationStarted;
}
public void abortCurrentGesture() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
index f50a287..4c69594 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
@@ -133,11 +133,11 @@
for (int i = 0; i < rowCount; i++) {
View row = rows.getChildAt(i);
if (to == ROTATION_SEASCAPE) {
- rotateSeekBars(row, to, 180);
- } else if (to == ROTATION_LANDSCAPE) {
rotateSeekBars(row, to, 0);
+ } else if (to == ROTATION_LANDSCAPE) {
+ rotateSeekBars(row, to, 180);
} else {
- rotateSeekBars(row, to, 270);
+ rotateSeekBars(row, to, 90);
}
rotate(row, from, to, true);
}
@@ -309,12 +309,6 @@
return super.getOutlineProvider();
}
- @Override
- public void setPressed(boolean pressed)
- {
- // Ignore presses because it activates the seekbar thumb unnecessarily.
- }
-
public void setOutsideTouchListener(OnClickListener onClickListener) {
mHasOutsideTouch = true;
requestLayout();
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index f6a54af..c77dcc0 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -63,7 +63,7 @@
// Number scans that returned at least one result.
optional int32 num_non_empty_scan_results = 13;
- // Number of scans that were one time.
+ // Number of single scans requests.
optional int32 num_oneshot_scans = 14;
// Number of repeated background scans that were scheduled to the chip.
@@ -376,6 +376,9 @@
// Wifi power statistics
optional WifiPowerStats wifi_power_stats = 92;
+
+ // Number of connectivity single scan requests.
+ optional int32 num_connectivity_oneshot_scans = 93;
}
// Information that gets logged for every WiFi connection.
@@ -1155,4 +1158,4 @@
// Amount of time wifi is in tx (ms)
optional int64 tx_time_ms = 5;
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 219facd..0502117 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -34,7 +34,7 @@
2731 power_soft_sleep_requested (savedwaketimems|2)
# Power save state has changed. See BatterySaverController.java for the details.
2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5)
-27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|6),(total_duration|2|3),(total_battery_drain|1|6)
+27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|1),(delta_battery_drain_percent|1|6),(total_duration|2|3),(total_battery_drain|1|1),(total_battery_drain_percent|1|6)
#
# Leave IDs through 2740 for more power logs (2730 used by battery_discharge above)
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 1dd92f3..65e90bad 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1397,23 +1397,6 @@
}
/**
- * Returns "true" if access to the specified location provider is allowed by the specified
- * user's settings. Access to all location providers is forbidden to non-location-provider
- * processes belonging to background users.
- *
- * @param provider the name of the location provider
- * @param uid the requestor's UID
- * @param userId the user id to query
- */
- private boolean isAllowedByUserSettingsLockedForUser(
- String provider, int uid, int userId) {
- if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
- return false;
- }
- return isLocationProviderEnabledForUser(provider, userId);
- }
-
- /**
* Returns the permission string associated with the specified resolution level.
*
* @param resolutionLevel the resolution level
@@ -2585,143 +2568,6 @@
}
/**
- * Method for enabling or disabling location.
- *
- * @param enabled true to enable location. false to disable location
- * @param userId the user id to set
- */
- @Override
- public void setLocationEnabledForUser(boolean enabled, int userId) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // Enable or disable all location providers. Fused provider and passive provider are
- // excluded.
- synchronized (mLock) {
- for(String provider : getAllProvidersForLocationSettings()) {
- setProviderEnabledForUser(provider, enabled, userId);
- }
- }
- }
-
- /**
- * Returns the current enabled/disabled status of location
- *
- * @param userId the user id to query
- * @return true if location is enabled. false if location is disabled.
- */
- @Override
- public boolean isLocationEnabledForUser(int userId) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // If at least one location provider is enabled, return true. Fused provider and passive
- // provider are excluded.
- synchronized (mLock) {
- for (String provider : getAllProvidersForLocationSettings()) {
- if (isProviderEnabledForUser(provider, userId)) {
- return true;
- }
- }
- return false;
- }
- }
-
- @Override
- public boolean isProviderEnabled(String provider) {
- return isProviderEnabledForUser(provider, UserHandle.getCallingUserId());
- }
-
- /**
- * Method for determining if a location provider is enabled.
- *
- * @param provider the location provider to query
- * @param userId the user id to query
- * @return true if the provider is enabled
- */
- @Override
- public boolean isProviderEnabledForUser(String provider, int userId) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
- // so we discourage its use
- if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
-
- int uid = Binder.getCallingUid();
- long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- return p != null
- && isAllowedByUserSettingsLockedForUser(provider, uid, userId);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Method for enabling or disabling a single location provider.
- *
- * @param provider the name of the provider
- * @param enabled true to enable the provider. false to disable the provider
- * @param userId the user id to set
- * @return true if the value was set successfully. false on failure.
- */
- @Override
- public boolean setProviderEnabledForUser(
- String provider, boolean enabled, int userId) {
- mContext.enforceCallingPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS,
- "Requires WRITE_SECURE_SETTINGS permission");
-
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- final long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- // to ensure thread safety, we write the provider name with a '+' or '-'
- // and let the SettingsProvider handle it rather than reading and modifying
- // the list of enabled providers.
- if (enabled) {
- provider = "+" + provider;
- } else {
- provider = "-" + provider;
- }
- return Settings.Secure.putStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- provider,
- userId);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Return all location providers except fused provider and passive provider. These two
- * providers are not generating location by themselves, but only echo locations from other
- * providers.
- *
- * @return All location providers except fused provider and passive provider, including
- * providers that are not permitted to be accessed by the calling activity or are
- * currently disabled.
- */
- private List<String> getAllProvidersForLocationSettings() {
- List<String> providersForSettings = new ArrayList<>(mProviders.size());
- for (String provider : getAllProviders()) {
- if (provider.equals(LocationManager.PASSIVE_PROVIDER)) {
- continue;
- }
- providersForSettings.add(provider);
- }
- return providersForSettings;
- }
-
- /**
* Read location provider status from Settings.Secure
*
* @param provider the location provider to query
@@ -2742,23 +2588,6 @@
}
/**
- * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
- * current user id
- *
- * @param userId the user id to get or set value
- */
- private void checkInteractAcrossUsersPermission(int userId) {
- int uid = Binder.getCallingUid();
- if (UserHandle.getUserId(uid) != userId) {
- if (ActivityManager.checkComponentPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
- != PERMISSION_GRANTED) {
- throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
- }
- }
- }
-
- /**
* Returns "true" if the UID belongs to a bound location provider.
*
* @param uid the uid
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 6747be3..6743484 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -35,6 +35,7 @@
import android.telephony.CellInfo;
import android.telephony.CellLocation;
import android.telephony.DisconnectCause;
+import android.telephony.LocationAccessPolicy;
import android.telephony.PhoneStateListener;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
@@ -93,7 +94,8 @@
IPhoneStateListener callback;
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
- int callerUserId;
+ int callerUid;
+ int callerPid;
int events;
@@ -117,7 +119,7 @@
+ " callback=" + callback
+ " onSubscriptionsChangedListenererCallback="
+ onSubscriptionsChangedListenerCallback
- + " callerUserId=" + callerUserId + " subId=" + subId + " phoneId=" + phoneId
+ + " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId
+ " events=" + Integer.toHexString(events)
+ " canReadPhoneState=" + canReadPhoneState + "}";
}
@@ -356,6 +358,8 @@
public void addOnSubscriptionsChangedListener(String callingPackage,
IOnSubscriptionsChangedListener callback) {
int callerUserId = UserHandle.getCallingUserId();
+ mContext.getSystemService(AppOpsManager.class)
+ .checkPackage(Binder.getCallingUid(), callingPackage);
if (VDBG) {
log("listen oscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId()
+ " callerUserId=" + callerUserId + " callback=" + callback
@@ -399,7 +403,8 @@
r.onSubscriptionsChangedListenerCallback = callback;
r.callingPackage = callingPackage;
- r.callerUserId = callerUserId;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
r.events = 0;
r.canReadPhoneState = true; // permission has been enforced above
if (DBG) {
@@ -470,6 +475,8 @@
private void listen(String callingPackage, IPhoneStateListener callback, int events,
boolean notifyNow, int subId) {
int callerUserId = UserHandle.getCallingUserId();
+ mContext.getSystemService(AppOpsManager.class)
+ .checkPackage(Binder.getCallingUid(), callingPackage);
if (VDBG) {
log("listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events)
+ " notifyNow=" + notifyNow + " subId=" + subId + " myUserId="
@@ -514,7 +521,8 @@
r.callback = callback;
r.callingPackage = callingPackage;
- r.callerUserId = callerUserId;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
boolean isPhoneStateEvent = (events & (CHECK_PHONE_STATE_PERMISSION_MASK
| ENFORCE_PHONE_STATE_PERMISSION_MASK)) != 0;
r.canReadPhoneState = isPhoneStateEvent && canReadPhoneState(callingPackage);
@@ -572,8 +580,10 @@
try {
if (DBG_LOC) log("listen: mCellLocation = "
+ mCellLocation[phoneId]);
- r.callback.onCellLocationChanged(
- new Bundle(mCellLocation[phoneId]));
+ if (checkLocationAccess(r)) {
+ r.callback.onCellLocationChanged(
+ new Bundle(mCellLocation[phoneId]));
+ }
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -619,7 +629,9 @@
try {
if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
+ mCellInfo.get(phoneId));
- r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ if (checkLocationAccess(r)) {
+ r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ }
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -979,7 +991,8 @@
mCellInfo.set(phoneId, cellInfo);
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
- idMatch(r.subId, subId, phoneId)) {
+ idMatch(r.subId, subId, phoneId) &&
+ checkLocationAccess(r)) {
try {
if (DBG_LOC) {
log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r);
@@ -1262,7 +1275,8 @@
mCellLocation[phoneId] = cellLocation;
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
- idMatch(r.subId, subId, phoneId)) {
+ idMatch(r.subId, subId, phoneId) &&
+ checkLocationAccess(r)) {
try {
if (DBG_LOC) {
log("notifyCellLocation: cellLocation=" + cellLocation
@@ -1706,10 +1720,11 @@
boolean valid = false;
try {
foregroundUser = ActivityManager.getCurrentUser();
- valid = r.callerUserId == foregroundUser && r.matchPhoneStateListenerEvent(events);
+ valid = UserHandle.getUserId(r.callerUid) == foregroundUser
+ && r.matchPhoneStateListenerEvent(events);
if (DBG | DBG_LOC) {
log("validateEventsAndUserLocked: valid=" + valid
- + " r.callerUserId=" + r.callerUserId + " foregroundUser=" + foregroundUser
+ + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser
+ " r.events=" + r.events + " events=" + events);
}
} finally {
@@ -1741,6 +1756,16 @@
}
}
+ private boolean checkLocationAccess(Record r) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ return LocationAccessPolicy.canAccessCellLocation(mContext,
+ r.callingPackage, r.callerUid, r.callerPid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private void checkPossibleMissNotify(Record r, int phoneId) {
int events = r.events;
@@ -1788,7 +1813,9 @@
log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
+ mCellInfo.get(phoneId));
}
- r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ if (checkLocationAccess(r)) {
+ r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ }
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -1836,7 +1863,9 @@
try {
if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = "
+ mCellLocation[phoneId]);
- r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
+ if (checkLocationAccess(r)) {
+ r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
+ }
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5b8b691..8d1632a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -802,6 +802,7 @@
doDump(fd, pw, new String[]{"recents"}, asProto);
doDump(fd, pw, new String[]{"lastanr"}, asProto);
doDump(fd, pw, new String[]{"starter"}, asProto);
+ doDump(fd, pw, new String[]{"containers"}, asProto);
if (mAssociations.size() > 0) {
doDump(fd, pw, new String[]{"associations"}, asProto);
}
@@ -4027,9 +4028,13 @@
runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
- if ("true".equals(genDebugInfoProperty)) {
+ if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
}
+ String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
+ if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
+ runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
+ }
if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
}
@@ -4331,7 +4336,9 @@
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
component.userId, component.realActivity.getPackageName(),
- component.realActivity.getShortClassName(), resumed ? 1 : 0);
+ component.realActivity.getShortClassName(), resumed ?
+ StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_FOREGROUND :
+ StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_BACKGROUND);
if (resumed) {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(component.realActivity, component.userId,
@@ -12844,7 +12851,8 @@
}
// TODO: Where should the corresponding '1' (start) write go?
- StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED, 0);
+ StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED,
+ StatsLog.DEVICE_ON_STATUS_CHANGED__STATE__OFF);
boolean timedout = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index f0c90e0..24a77c7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -115,6 +115,7 @@
private int mActivityType;
private int mTaskId;
private boolean mIsTaskOverlay;
+ private boolean mIsLockTask;
final boolean mDumping;
@@ -278,6 +279,7 @@
mActivityType = ACTIVITY_TYPE_UNDEFINED;
mTaskId = INVALID_TASK_ID;
mIsTaskOverlay = false;
+ mIsLockTask = false;
return Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() {
@Override
@@ -334,6 +336,8 @@
mTaskId = Integer.parseInt(getNextArgRequired());
} else if (opt.equals("--task-overlay")) {
mIsTaskOverlay = true;
+ } else if (opt.equals("--lock-task")) {
+ mIsLockTask = true;
} else {
return false;
}
@@ -429,13 +433,22 @@
options.setLaunchActivityType(mActivityType);
}
if (mTaskId != INVALID_TASK_ID) {
- options = ActivityOptions.makeBasic();
+ if (options == null) {
+ options = ActivityOptions.makeBasic();
+ }
options.setLaunchTaskId(mTaskId);
if (mIsTaskOverlay) {
options.setTaskOverlay(true, true /* canResume */);
}
}
+ android.util.Log.d("bfranz", "I was here: " + mIsLockTask);
+ if (mIsLockTask) {
+ if (options == null) {
+ options = ActivityOptions.makeBasic();
+ }
+ options.setLockTaskMode(true);
+ }
if (mWaitOption) {
result = mInterface.startActivityAndWait(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo,
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0d96468..ea52782 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -325,39 +325,38 @@
void noteProcessStart(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessStartLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 1);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_STARTED);
}
}
void noteProcessCrash(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessCrashLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 2);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_CRASHED);
}
}
void noteProcessAnr(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessAnrLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 3);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_ANRED);
}
}
void noteProcessFinish(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessFinishLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 0);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_FINISHED);
}
}
/** @param state Process state from ActivityManager.java. */
void noteUidProcessState(int uid, int state) {
synchronized (mStats) {
- // TODO: remove this once we figure out properly where and how
StatsLog.write(StatsLog.UID_PROCESS_STATE_CHANGED, uid,
ActivityManager.processStateAmToProto(state));
@@ -603,7 +602,6 @@
enforceCallingPermission();
if (DBG) Slog.d(TAG, "begin noteScreenState");
synchronized (mStats) {
- // TODO: remove this once we figure out properly where and how
StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, state);
mStats.noteScreenStateLocked(state);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index e0baeee..401c05e 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -862,7 +862,8 @@
}
startTrackingJobLocked(jobStatus, toCancel);
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
- uId, null, jobStatus.getBatteryName(), 2);
+ uId, null, jobStatus.getBatteryName(),
+ StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED);
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 28fa86b..e715724 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -383,8 +383,8 @@
return KeyStore.getInstance();
}
- public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
- return RecoverableKeyStoreManager.getInstance(mContext);
+ public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) {
+ return RecoverableKeyStoreManager.getInstance(mContext, keyStore);
}
public IStorageManager getStorageManager() {
@@ -413,7 +413,7 @@
mInjector = injector;
mContext = injector.getContext();
mKeyStore = injector.getKeyStore();
- mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
+ mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager(mKeyStore);
mHandler = injector.getHandler();
mStrongAuth = injector.getStrongAuth();
mActivityManager = injector.getActivityManager();
@@ -1887,7 +1887,7 @@
mSpManager.removeUser(userId);
mStorage.removeUser(userId);
mStrongAuth.removeUser(userId);
- cleanSpCache();
+ tryRemoveUserFromSpCacheLater(userId);
final KeyStore ks = KeyStore.getInstance();
ks.onUserRemoved(userId);
@@ -2064,6 +2064,16 @@
return mRecoverableKeyStoreManager.generateAndStoreKey(alias);
}
+ @Override
+ public String generateKey(@NonNull String alias, byte[] account) throws RemoteException {
+ return mRecoverableKeyStoreManager.generateKey(alias, account);
+ }
+
+ @Override
+ public String getKey(@NonNull String alias) throws RemoteException {
+ return mRecoverableKeyStoreManager.getKey(alias);
+ }
+
private static final String[] VALID_SETTINGS = new String[] {
LockPatternUtils.LOCKOUT_PERMANENT_KEY,
LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
@@ -2141,6 +2151,13 @@
private SparseArray<AuthenticationToken> mSpCache = new SparseArray();
private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) {
+ // Preemptively cache the SP and then try to remove it in a handler.
+ Slog.i(TAG, "Caching SP for user " + userId);
+ synchronized (mSpManager) {
+ mSpCache.put(userId, auth);
+ }
+ tryRemoveUserFromSpCacheLater(userId);
+
// Pass the primary user's auth secret to the HAL
if (mAuthSecretService != null && mUserManager.getUserInfo(userId).isPrimary()) {
try {
@@ -2154,33 +2171,25 @@
Slog.w(TAG, "Failed to pass primary user secret to AuthSecret HAL", e);
}
}
-
- // Update the SP cache, removing the entry when allowed
- synchronized (mSpManager) {
- if (shouldCacheSpForUser(userId)) {
- Slog.i(TAG, "Caching SP for user " + userId);
- mSpCache.put(userId, auth);
- } else {
- Slog.i(TAG, "Not caching SP for user " + userId);
- mSpCache.delete(userId);
- }
- }
}
- /** Clean up the SP cache by removing unneeded entries. */
- private void cleanSpCache() {
- synchronized (mSpManager) {
- // Preserve indicies after removal by iterating backwards
- for (int i = mSpCache.size() - 1; i >= 0; --i) {
- final int userId = mSpCache.keyAt(i);
- if (!shouldCacheSpForUser(userId)) {
- Slog.i(TAG, "Uncaching SP for user " + userId);
- mSpCache.removeAt(i);
+ private void tryRemoveUserFromSpCacheLater(@UserIdInt int userId) {
+ mHandler.post(() -> {
+ if (!shouldCacheSpForUser(userId)) {
+ // The transition from 'should not cache' to 'should cache' can only happen if
+ // certain admin apps are installed after provisioning e.g. via adb. This is not
+ // a common case and we do not seamlessly support; it may result in the SP not
+ // being cached when it is needed. The cache can be re-populated by verifying
+ // the credential again.
+ Slog.i(TAG, "Removing SP from cache for user " + userId);
+ synchronized (mSpManager) {
+ mSpCache.remove(userId);
}
}
- }
+ });
}
+ /** Do not hold any of the locks from this service when calling. */
private boolean shouldCacheSpForUser(@UserIdInt int userId) {
// Before the user setup has completed, an admin could be installed that requires the SP to
// be cached (see below).
@@ -2731,7 +2740,7 @@
}
@Override
- public void onChange(boolean selfChange, Uri uri) {
+ public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
if (mDeviceProvisionedUri.equals(uri)) {
updateRegistration();
@@ -2741,7 +2750,7 @@
clearFrpCredentialIfOwnerNotSecure();
}
} else if (mUserSetupCompleteUri.equals(uri)) {
- cleanSpCache();
+ tryRemoveUserFromSpCacheLater(userId);
}
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
index 59132da..285e722 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
@@ -16,10 +16,13 @@
package com.android.server.locksettings.recoverablekeystore;
+import java.io.IOException;
+import java.security.cert.CertificateException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
/**
@@ -27,6 +30,7 @@
*/
public class KeyStoreProxyImpl implements KeyStoreProxy {
+ private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private final KeyStore mKeyStore;
/**
@@ -57,4 +61,21 @@
public void deleteEntry(String alias) throws KeyStoreException {
mKeyStore.deleteEntry(alias);
}
+
+ /**
+ * Returns AndroidKeyStore-provided {@link KeyStore}, having already invoked
+ * {@link KeyStore#load(KeyStore.LoadStoreParameter)}.
+ *
+ * @throws KeyStoreException if there was a problem getting or initializing the key store.
+ */
+ public static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException {
+ KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+ try {
+ keyStore.load(/*param=*/ null);
+ } catch (CertificateException | IOException | NoSuchAlgorithmException e) {
+ // Should never happen.
+ throw new KeyStoreException("Unable to load keystore.", e);
+ }
+ return keyStore;
+ }
}
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 ec72b22..fda6cdf 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -37,21 +37,23 @@
import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.RecoveryController;
import android.security.keystore.recovery.WrappedApplicationKey;
+import android.security.KeyStore;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
+import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
import java.security.InvalidKeyException;
-import java.security.KeyStoreException;
import java.security.KeyFactory;
+import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
-import java.security.UnrecoverableKeyException;
import java.security.spec.InvalidKeySpecException;
+import java.security.UnrecoverableKeyException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
@@ -82,18 +84,23 @@
private final RecoverableKeyGenerator mRecoverableKeyGenerator;
private final RecoverySnapshotStorage mSnapshotStorage;
private final PlatformKeyManager mPlatformKeyManager;
+ private final KeyStore mKeyStore;
+ private final ApplicationKeyStorage mApplicationKeyStorage;
/**
* Returns a new or existing instance.
*
* @hide
*/
- public static synchronized RecoverableKeyStoreManager getInstance(Context context) {
+ public static synchronized RecoverableKeyStoreManager
+ getInstance(Context context, KeyStore keystore) {
if (mInstance == null) {
RecoverableKeyStoreDb db = RecoverableKeyStoreDb.newInstance(context);
PlatformKeyManager platformKeyManager;
+ ApplicationKeyStorage applicationKeyStorage;
try {
platformKeyManager = PlatformKeyManager.getInstance(context, db);
+ applicationKeyStorage = ApplicationKeyStorage.getInstance(keystore);
} catch (NoSuchAlgorithmException e) {
// Impossible: all algorithms must be supported by AOSP
throw new RuntimeException(e);
@@ -103,12 +110,14 @@
mInstance = new RecoverableKeyStoreManager(
context.getApplicationContext(),
+ keystore,
db,
new RecoverySessionStorage(),
Executors.newSingleThreadExecutor(),
new RecoverySnapshotStorage(),
new RecoverySnapshotListenersStorage(),
- platformKeyManager);
+ platformKeyManager,
+ applicationKeyStorage);
}
return mInstance;
}
@@ -116,19 +125,23 @@
@VisibleForTesting
RecoverableKeyStoreManager(
Context context,
+ KeyStore keystore,
RecoverableKeyStoreDb recoverableKeyStoreDb,
RecoverySessionStorage recoverySessionStorage,
ExecutorService executorService,
RecoverySnapshotStorage snapshotStorage,
RecoverySnapshotListenersStorage listenersStorage,
- PlatformKeyManager platformKeyManager) {
+ PlatformKeyManager platformKeyManager,
+ ApplicationKeyStorage applicationKeyStorage) {
mContext = context;
+ mKeyStore = keystore;
mDatabase = recoverableKeyStoreDb;
mRecoverySessionStorage = recoverySessionStorage;
mExecutorService = executorService;
mListenersStorage = listenersStorage;
mSnapshotStorage = snapshotStorage;
mPlatformKeyManager = platformKeyManager;
+ mApplicationKeyStorage = applicationKeyStorage;
try {
mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
@@ -406,6 +419,7 @@
}
/**
+ * Deprecated
* Generates a key named {@code alias} in the recoverable store for the calling uid. Then
* returns the raw key material.
*
@@ -450,9 +464,55 @@
boolean wasRemoved = mDatabase.removeKey(uid, alias);
if (wasRemoved) {
mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ mApplicationKeyStorage.deleteEntry(userId, uid, alias);
}
}
+ /**
+ * Generates a key named {@code alias} in caller's namespace.
+ * The key is stored in system service keystore namespace.
+ *
+ * @return grant alias, which caller can use to access the key.
+ */
+ public String generateKey(@NonNull String alias, byte[] account) throws RemoteException {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+
+ PlatformEncryptionKey encryptionKey;
+ try {
+ encryptionKey = mPlatformKeyManager.getEncryptKey(userId);
+ } catch (NoSuchAlgorithmException e) {
+ // Impossible: all algorithms must be supported by AOSP
+ throw new RuntimeException(e);
+ } catch (KeyStoreException | UnrecoverableKeyException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ } catch (InsecureUserException e) {
+ throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage());
+ }
+
+ try {
+ byte[] secretKey =
+ mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias);
+ mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey);
+ String grantAlias = mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+ return grantAlias;
+ } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ }
+ }
+
+ /**
+ * Gets a key named {@code alias} in caller's namespace.
+ *
+ * @return grant alias, which caller can use to access the key.
+ */
+ public String getKey(@NonNull String alias) throws RemoteException {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ String grantAlias = mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+ return grantAlias;
+ }
+
private byte[] decryptRecoveryKey(
RecoverySessionStorage.Entry sessionEntry, byte[] encryptedClaimResponse)
throws RemoteException, ServiceSpecificException {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
new file mode 100644
index 0000000..600a534
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+package com.android.server.locksettings.recoverablekeystore.storage;
+
+import static android.security.keystore.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR;
+
+import android.annotation.Nullable;
+import android.os.ServiceSpecificException;
+import android.security.Credentials;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.KeyStore;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.recoverablekeystore.KeyStoreProxy;
+import com.android.server.locksettings.recoverablekeystore.KeyStoreProxyImpl;
+
+import java.security.KeyStore.SecretKeyEntry;
+import java.security.KeyStoreException;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Storage for Application keys in LockSettings service KeyStore namespace.
+ *
+ * <p> Uses KeyStore's grant mechanism to make keys usable by application process without
+ * revealing key material
+ */
+public class ApplicationKeyStorage {
+ private static final String APPLICATION_KEY_ALIAS_PREFIX =
+ "com.android.server.locksettings.recoverablekeystore/application/";
+
+ KeyStoreProxy mKeyStore;
+ KeyStore mKeystoreService;
+
+ public static ApplicationKeyStorage getInstance(KeyStore keystoreService)
+ throws KeyStoreException {
+ return new ApplicationKeyStorage(
+ new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore()),
+ keystoreService);
+ }
+
+ @VisibleForTesting
+ ApplicationKeyStorage(KeyStoreProxy keyStore, KeyStore keystoreService) {
+ mKeyStore = keyStore;
+ mKeystoreService = keystoreService;
+ }
+
+ /**
+ * Returns grant alias, valid in Applications namespace.
+ */
+ public @Nullable String getGrantAlias(int userId, int uid, String alias) {
+ // Aliases used by {@link KeyStore} are different than used by public API.
+ // {@code USER_PRIVATE_KEY} prefix is used secret keys.
+ String keystoreAlias = Credentials.USER_PRIVATE_KEY + getInternalAlias(userId, uid, alias);
+ return mKeystoreService.grant(keystoreAlias, uid);
+ }
+
+ public void setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey)
+ throws KeyStoreException {
+ try {
+ mKeyStore.setEntry(
+ getInternalAlias(userId, uid, alias),
+ new SecretKeyEntry(
+ new SecretKeySpec(secretKey, KeyProperties.KEY_ALGORITHM_AES)),
+ new KeyProtection.Builder(
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ } catch (KeyStoreException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ }
+ }
+
+ public void deleteEntry(int userId, int uid, String alias) {
+ try {
+ mKeyStore.deleteEntry(getInternalAlias(userId, uid, alias));
+ } catch (KeyStoreException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ }
+ }
+
+ /**
+ * Returns the alias in locksettins service's KeyStore namespace used for given application key.
+ *
+ * <p>These IDs look as follows:
+ * {@code com.security.recoverablekeystore/application/<userId>/<uid>/<alias>}
+ *
+ * @param userId The ID of the user
+ * @param uid The uid
+ * @param alias - alias in application's namespace
+ * @return The alias.
+ */
+ private String getInternalAlias(int userId, int uid, String alias) {
+ return APPLICATION_KEY_ALIAS_PREFIX + userId + "/" + uid + "/" + alias;
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 502760a..fd435f9 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -771,7 +771,7 @@
* Called whenever packages change, the user switches, or the secure setting
* is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
*/
- private void rebindServices(boolean forceRebind) {
+ protected void rebindServices(boolean forceRebind) {
if (DEBUG) Slog.d(TAG, "rebindServices");
final int[] userIds = mUserProfiles.getCurrentProfileIds();
final int nUserIds = userIds.length;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 727e7ee..b25124a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2891,6 +2891,7 @@
// Backup/restore interface
@Override
public byte[] getBackupPayload(int user) {
+ checkCallerIsSystem();
if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
//TODO: http://b/22388012
if (user != UserHandle.USER_SYSTEM) {
@@ -2911,6 +2912,7 @@
@Override
public void applyRestore(byte[] payload, int user) {
+ checkCallerIsSystem();
if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
+ (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
if (payload == null) {
@@ -5687,6 +5689,12 @@
mListeners.unregisterService(removed.service, removed.userid);
}
+ @Override
+ public void onUserUnlocked(int user) {
+ if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
+ rebindServices(true);
+ }
+
public void onNotificationEnqueued(final NotificationRecord r) {
final StatusBarNotification sbn = r.sbn;
TrimCache trimCache = new TrimCache(sbn);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index c3f20af..b79caca 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -290,13 +290,14 @@
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
@Nullable String seInfo, boolean downgrade, int targetSdkVersion,
- @Nullable String profileName) throws InstallerException {
+ @Nullable String profileName, @Nullable String dexMetadataPath)
+ throws InstallerException {
assertValidInstructionSet(instructionSet);
if (!checkBeforeRemote()) return;
try {
mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
- targetSdkVersion, profileName);
+ targetSdkVersion, profileName, dexMetadataPath);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 10e05cf..fc73142 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -261,12 +261,12 @@
String instructionSet, int dexoptNeeded, @Nullable String outputPath,
int dexFlags, String compilerFilter, @Nullable String volumeUuid,
@Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade,
- int targetSdkVersion, @Nullable String profileName)
- throws InstallerException {
+ int targetSdkVersion, @Nullable String profileName,
+ @Nullable String dexMetadataPath) throws InstallerException {
final StringBuilder builder = new StringBuilder();
- // The version. Right now it's 5.
- builder.append("5 ");
+ // The version. Right now it's 6.
+ builder.append("6 ");
builder.append("dexopt");
@@ -284,6 +284,7 @@
encodeParameter(builder, downgrade);
encodeParameter(builder, targetSdkVersion);
encodeParameter(builder, profileName);
+ encodeParameter(builder, dexMetadataPath);
commands.add(builder.toString());
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index cde8cb7..2c68e67 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -21,6 +21,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.content.pm.dex.ArtManager;
+import android.content.pm.dex.DexMetadataHelper;
import android.os.FileUtils;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -209,6 +210,13 @@
String profileName = ArtManager.getProfileName(i == 0 ? null : pkg.splitNames[i - 1]);
+ String dexMetadataPath = null;
+ if (options.isDexoptInstallWithDexMetadata()) {
+ File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path));
+ dexMetadataPath = dexMetadataFile == null
+ ? null : dexMetadataFile.getAbsolutePath();
+ }
+
final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
|| packageUseInfo.isUsedByOtherApps(path);
final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
@@ -223,7 +231,7 @@
for (String dexCodeIsa : dexCodeInstructionSets) {
int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
- packageStats, options.isDowngrade(), profileName);
+ packageStats, options.isDowngrade(), profileName, dexMetadataPath);
// The end result is:
// - FAILED if any path failed,
// - PERFORMED if at least one path needed compilation,
@@ -248,7 +256,7 @@
private int dexOptPath(PackageParser.Package pkg, String path, String isa,
String compilerFilter, boolean profileUpdated, String classLoaderContext,
int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
- String profileName) {
+ String profileName, String dexMetadataPath) {
int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
profileUpdated, downgrade);
if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
@@ -275,7 +283,7 @@
mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
false /* downgrade*/, pkg.applicationInfo.targetSdkVersion,
- profileName);
+ profileName, dexMetadataPath);
if (packageStats != null) {
long endTime = System.currentTimeMillis();
@@ -396,7 +404,8 @@
mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
/*oatDir*/ null, dexoptFlags,
compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
- options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null);
+ options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null,
+ /*dexMetadataPath*/ null);
}
return DEX_OPT_PERFORMED;
@@ -511,9 +520,13 @@
private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
int flags = info.flags;
boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- // Profile guide compiled oat files should not be public.
+ // Profile guide compiled oat files should not be public unles they are based
+ // on profiles from dex metadata archives.
+ // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
+ // the user does not have an existing profile.
boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
- boolean isPublic = !info.isForwardLocked() && !isProfileGuidedFilter;
+ boolean isPublic = !info.isForwardLocked() &&
+ (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
// System apps are invoked with a runtime flag which exempts them from
// restrictions on hidden API usage. We dexopt with the same runtime flag
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89fbd17..82cd2d9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17309,7 +17309,8 @@
// Also, don't fail application installs if the dexopt step fails.
DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
REASON_INSTALL,
- DexoptOptions.DEXOPT_BOOT_COMPLETE);
+ DexoptOptions.DEXOPT_BOOT_COMPLETE |
+ DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
null /* instructionSets */,
getOrCreateCompilerPackageStats(pkg),
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index c02331d..5060c4d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -693,7 +693,7 @@
InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
) {
- Streams.copy(fileIn, fileOut);
+ FileUtils.copy(fileIn, fileOut);
Os.chmod(dstFile.getAbsolutePath(), 0644);
return PackageManager.INSTALL_SUCCEEDED;
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index ebf6672..e4c74ed 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -703,7 +703,7 @@
*/
public boolean rescanPackageIfNeeded(boolean isNewApp, boolean forceRescan) {
final ShortcutService s = mShortcutUser.mService;
- final long start = s.injectElapsedRealtime();
+ final long start = s.getStatStartTime();
final PackageInfo pi;
try {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index d2bc6d2..a85d6d8 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -99,6 +99,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.StatLogger;
import com.android.server.SystemService;
import com.android.server.pm.ShortcutUser.PackageWithUser;
@@ -367,7 +368,7 @@
int COUNT = GET_DEFAULT_LAUNCHER + 1;
}
- private static final String[] STAT_LABELS = {
+ private final StatLogger mStatLogger = new StatLogger(new String[] {
"getHomeActivities()",
"Launcher permission check",
"getPackageInfo()",
@@ -385,15 +386,7 @@
"packageUpdateCheck",
"asyncPreloadUserDelay",
"getDefaultLauncher()"
- };
-
- final Object mStatLock = new Object();
-
- @GuardedBy("mStatLock")
- private final int[] mCountStats = new int[Stats.COUNT];
-
- @GuardedBy("mStatLock")
- private final long[] mDurationStats = new long[Stats.COUNT];
+ });
private static final int PROCESS_STATE_FOREGROUND_THRESHOLD =
ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
@@ -480,11 +473,12 @@
| ActivityManager.UID_OBSERVER_GONE);
}
+ long getStatStartTime() {
+ return mStatLogger.getTime();
+ }
+
void logDurationStat(int statId, long start) {
- synchronized (mStatLock) {
- mCountStats[statId]++;
- mDurationStats[statId] += (injectElapsedRealtime() - start);
- }
+ mStatLogger.logDurationStat(statId, start);
}
public String injectGetLocaleTagsForUser(@UserIdInt int userId) {
@@ -621,7 +615,7 @@
// late since the launcher would already have started.
// So we just create a new thread. This code runs rarely, so we don't use a thread pool
// or anything.
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
injectRunOnNewThread(() -> {
synchronized (mLock) {
logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start);
@@ -1289,7 +1283,7 @@
if (DEBUG) {
Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId);
}
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final ShortcutUser user = getUserShortcutsLocked(userId);
@@ -1485,7 +1479,7 @@
final Resources publisherRes = injectGetResourcesForApplicationAsUser(
si.getPackage(), si.getUserId());
if (publisherRes != null) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
si.lookupAndFillInResourceNames(publisherRes);
} finally {
@@ -2264,7 +2258,7 @@
if (canSeeAnyPinnedShortcut(callingPackage, userId, callingPid, callingUid)) {
return true;
}
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
return hasShortcutHostPermissionInner(callingPackage, userId);
} finally {
@@ -2327,7 +2321,7 @@
@Nullable
ComponentName getDefaultLauncher(@UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
synchronized (mLock) {
@@ -2338,7 +2332,7 @@
final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
// Default launcher from package manager.
- final long startGetHomeActivitiesAsUser = injectElapsedRealtime();
+ final long startGetHomeActivitiesAsUser = getStatStartTime();
final ComponentName defaultLauncher = mPackageManagerInternal
.getHomeActivitiesAsUser(allHomeCandidates, userId);
logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser);
@@ -2910,7 +2904,7 @@
return;
}
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
@@ -3087,7 +3081,7 @@
@VisibleForTesting
PackageInfo injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId,
boolean getSignatures) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
return mIPackageManager.getPackageInfo(
@@ -3122,7 +3116,7 @@
@VisibleForTesting
ApplicationInfo injectApplicationInfoWithUninstalled(
String packageName, @UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId);
@@ -3153,7 +3147,7 @@
@VisibleForTesting
ActivityInfo injectGetActivityInfoWithMetadataWithUninstalled(
ComponentName activity, @UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
return mIPackageManager.getActivityInfo(activity,
@@ -3175,7 +3169,7 @@
@NonNull
@VisibleForTesting
final List<PackageInfo> getInstalledPackages(@UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
final List<PackageInfo> all = injectGetPackagesWithUninstalled(userId);
@@ -3280,7 +3274,7 @@
@Nullable
Resources injectGetResourcesForApplicationAsUser(String packageName, int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
return mContext.getPackageManager().getResourcesForApplicationAsUser(
@@ -3348,7 +3342,7 @@
*/
@Nullable
ComponentName injectGetDefaultMainActivity(@NonNull String packageName, int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
final List<ResolveInfo> resolved =
queryActivities(getMainActivityIntent(), packageName, null, userId);
@@ -3362,7 +3356,7 @@
* Return whether an activity is enabled, exported and main.
*/
boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
if (activity == null) {
wtf("null activity detected");
@@ -3397,7 +3391,7 @@
*/
@NonNull
List<ResolveInfo> injectGetMainActivities(@NonNull String packageName, int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
return queryActivities(getMainActivityIntent(), packageName, null, userId);
} finally {
@@ -3411,7 +3405,7 @@
@VisibleForTesting
boolean injectIsActivityEnabledAndExported(
@NonNull ComponentName activity, @UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
return queryActivities(new Intent(), activity.getPackageName(), activity, userId)
.size() > 0;
@@ -3831,12 +3825,7 @@
pw.println(mMaxShortcuts);
pw.println();
- pw.println(" Stats:");
- synchronized (mStatLock) {
- for (int i = 0; i < Stats.COUNT; i++) {
- dumpStatLS(pw, " ", i);
- }
- }
+ mStatLogger.dump(pw, " ");
pw.println();
pw.print(" #Failures: ");
@@ -3902,15 +3891,6 @@
pw.print(formatTime(injectCurrentTimeMillis()));
}
- private void dumpStatLS(PrintWriter pw, String prefix, int statId) {
- pw.print(prefix);
- final int count = mCountStats[statId];
- final long dur = mDurationStats[statId];
- pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms",
- STAT_LABELS[statId], count, dur,
- (count == 0 ? 0 : ((double) dur) / count)));
- }
-
/**
* Dumpsys for checkin.
*
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 92fd904..b53d83b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -88,6 +88,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
@@ -387,7 +388,9 @@
}
final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT);
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
- setQuietModeEnabled(userHandle, false, target);
+ // Call setQuietModeEnabled on bg thread to avoid ANR
+ BackgroundThread.getHandler()
+ .post(() -> setQuietModeEnabled(userHandle, false, target));
}
};
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index a42fcbd..842f8d0 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -117,7 +117,7 @@
UserManager.DISALLOW_AUTOFILL,
UserManager.DISALLOW_USER_SWITCH,
UserManager.DISALLOW_UNIFIED_PASSWORD,
- UserManager.DISALLOW_CONFIG_LOCATION_MODE,
+ UserManager.DISALLOW_CONFIG_LOCATION,
UserManager.DISALLOW_AIRPLANE_MODE,
UserManager.DISALLOW_CONFIG_BRIGHTNESS,
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index 0966770..d4f95cb 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -59,6 +59,10 @@
// When set, indicates that dexopt is invoked from the background service.
public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
+ // When set, indicates that dexopt is invoked from the install time flow and
+ // should get the dex metdata file if present.
+ public static final int DEXOPT_INSTALL_WITH_DEX_METADATA_FILE = 1 << 10;
+
// The name of package to optimize.
private final String mPackageName;
@@ -90,7 +94,8 @@
DEXOPT_ONLY_SHARED_DEX |
DEXOPT_DOWNGRADE |
DEXOPT_AS_SHARED_LIBRARY |
- DEXOPT_IDLE_BACKGROUND_JOB;
+ DEXOPT_IDLE_BACKGROUND_JOB |
+ DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
if ((flags & (~validityMask)) != 0) {
throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
}
@@ -141,6 +146,10 @@
return (mFlags & DEXOPT_IDLE_BACKGROUND_JOB) != 0;
}
+ public boolean isDexoptInstallWithDexMetadata() {
+ return (mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) != 0;
+ }
+
public String getSplitName() {
return mSplitName;
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index 9466350..b0b07ea 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -32,6 +32,8 @@
/**
* This class keeps track of battery drain rate.
*
+ * TODO: The use of the terms "percent" and "level" in this class is not standard. Fix it.
+ *
* Test:
atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
*/
@@ -96,8 +98,12 @@
public int startBatteryLevel;
public int endBatteryLevel;
+ public int startBatteryPercent;
+ public int endBatteryPercent;
+
public long totalTimeMillis;
public int totalBatteryDrain;
+ public int totalBatteryDrainPercent;
public long totalMinutes() {
return totalTimeMillis / 60_000;
@@ -110,14 +116,26 @@
return (double) totalBatteryDrain / (totalTimeMillis / (60.0 * 60 * 1000));
}
+ public double drainPercentPerHour() {
+ if (totalTimeMillis == 0) {
+ return 0;
+ }
+ return (double) totalBatteryDrainPercent / (totalTimeMillis / (60.0 * 60 * 1000));
+ }
+
@VisibleForTesting
String toStringForTest() {
return "{" + totalMinutes() + "m," + totalBatteryDrain + ","
- + String.format("%.2f", drainPerHour()) + "}";
+ + String.format("%.2f", drainPerHour()) + "uA/H,"
+ + String.format("%.2f", drainPercentPerHour()) + "%"
+ + "}";
}
}
@VisibleForTesting
+ static final String COUNTER_POWER_PERCENT_PREFIX = "battery_saver_stats_percent_";
+
+ @VisibleForTesting
static final String COUNTER_POWER_MILLIAMPS_PREFIX = "battery_saver_stats_milliamps_";
@VisibleForTesting
@@ -166,6 +184,9 @@
private BatteryManagerInternal getBatteryManagerInternal() {
if (mBatteryManagerInternal == null) {
mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+ if (mBatteryManagerInternal == null) {
+ Slog.wtf(TAG, "BatteryManagerInternal not initialized");
+ }
}
return mBatteryManagerInternal;
}
@@ -229,12 +250,20 @@
int injectBatteryLevel() {
final BatteryManagerInternal bmi = getBatteryManagerInternal();
if (bmi == null) {
- Slog.wtf(TAG, "BatteryManagerInternal not initialized");
return 0;
}
return bmi.getBatteryChargeCounter();
}
+ @VisibleForTesting
+ int injectBatteryPercent() {
+ final BatteryManagerInternal bmi = getBatteryManagerInternal();
+ if (bmi == null) {
+ return 0;
+ }
+ return bmi.getBatteryLevel();
+ }
+
/**
* Called from the outside whenever any of the states changes, when the device is not plugged
* in.
@@ -262,33 +291,39 @@
}
final long now = injectCurrentTime();
final int batteryLevel = injectBatteryLevel();
+ final int batteryPercent = injectBatteryPercent();
- endLastStateLocked(now, batteryLevel);
- startNewStateLocked(newState, now, batteryLevel);
- mMetricsLoggerHelper.transitionState(newState, now, batteryLevel);
+ endLastStateLocked(now, batteryLevel, batteryPercent);
+ startNewStateLocked(newState, now, batteryLevel, batteryPercent);
+ mMetricsLoggerHelper.transitionState(newState, now, batteryLevel, batteryPercent);
}
- private void endLastStateLocked(long now, int batteryLevel) {
+ private void endLastStateLocked(long now, int batteryLevel, int batteryPercent) {
if (mCurrentState < 0) {
return;
}
final Stat stat = getStat(mCurrentState);
stat.endBatteryLevel = batteryLevel;
+ stat.endBatteryPercent = batteryPercent;
stat.endTime = now;
final long deltaTime = stat.endTime - stat.startTime;
final int deltaDrain = stat.startBatteryLevel - stat.endBatteryLevel;
+ final int deltaPercent = stat.startBatteryPercent - stat.endBatteryPercent;
stat.totalTimeMillis += deltaTime;
stat.totalBatteryDrain += deltaDrain;
+ stat.totalBatteryDrainPercent += deltaPercent;
if (DEBUG) {
Slog.d(TAG, "State summary: " + stateToString(mCurrentState)
+ ": " + (deltaTime / 1_000) + "s "
+ "Start level: " + stat.startBatteryLevel + "uA "
+ "End level: " + stat.endBatteryLevel + "uA "
- + deltaDrain + "uA");
+ + "Start percent: " + stat.startBatteryPercent + "% "
+ + "End percent: " + stat.endBatteryPercent + "% "
+ + "Drain " + deltaDrain + "uA");
}
EventLogTags.writeBatterySavingStats(
BatterySaverState.fromIndex(mCurrentState),
@@ -296,12 +331,14 @@
DozeState.fromIndex(mCurrentState),
deltaTime,
deltaDrain,
+ deltaPercent,
stat.totalTimeMillis,
- stat.totalBatteryDrain);
+ stat.totalBatteryDrain,
+ stat.totalBatteryDrainPercent);
}
- private void startNewStateLocked(int newState, long now, int batteryLevel) {
+ private void startNewStateLocked(int newState, long now, int batteryLevel, int batteryPercent) {
if (DEBUG) {
Slog.d(TAG, "New state: " + stateToString(newState));
}
@@ -313,6 +350,7 @@
final Stat stat = getStat(mCurrentState);
stat.startBatteryLevel = batteryLevel;
+ stat.startBatteryPercent = batteryPercent;
stat.startTime = now;
stat.endTime = 0;
}
@@ -325,7 +363,7 @@
indent = indent + " ";
pw.print(indent);
- pw.println("Battery Saver: Off On");
+ pw.println("Battery Saver: Off On");
dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr",
DozeState.NOT_DOZING, "NonDoze");
dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr",
@@ -357,12 +395,14 @@
final Stat offStat = getStat(BatterySaverState.OFF, interactiveState, dozeState);
final Stat onStat = getStat(BatterySaverState.ON, interactiveState, dozeState);
- pw.println(String.format("%6dm %6dmA %8.1fmA/h %6dm %6dmA %8.1fmA/h",
+ pw.println(String.format("%6dm %6dmA (%3d%%) %8.1fmA/h %6dm %6dmA (%3d%%) %8.1fmA/h",
offStat.totalMinutes(),
offStat.totalBatteryDrain / 1000,
+ offStat.totalBatteryDrainPercent,
offStat.drainPerHour() / 1000.0,
onStat.totalMinutes(),
onStat.totalBatteryDrain / 1000,
+ onStat.totalBatteryDrainPercent,
onStat.drainPerHour() / 1000.0));
}
@@ -371,12 +411,13 @@
private int mLastState = STATE_NOT_INITIALIZED;
private long mStartTime;
private int mStartBatteryLevel;
+ private int mStartPercent;
private static final int STATE_CHANGE_DETECT_MASK =
(BatterySaverState.MASK << BatterySaverState.SHIFT) |
(InteractiveState.MASK << InteractiveState.SHIFT);
- public void transitionState(int newState, long now, int batteryLevel) {
+ public void transitionState(int newState, long now, int batteryLevel, int batteryPercent) {
final boolean stateChanging =
((mLastState >= 0) ^ (newState >= 0)) ||
(((mLastState ^ newState) & STATE_CHANGE_DETECT_MASK) != 0);
@@ -384,11 +425,13 @@
if (mLastState >= 0) {
final long deltaTime = now - mStartTime;
final int deltaBattery = mStartBatteryLevel - batteryLevel;
+ final int deltaPercent = mStartPercent - batteryPercent;
- report(mLastState, deltaTime, deltaBattery);
+ report(mLastState, deltaTime, deltaBattery, deltaPercent);
}
mStartTime = now;
mStartBatteryLevel = batteryLevel;
+ mStartPercent = batteryPercent;
}
mLastState = newState;
}
@@ -405,9 +448,10 @@
}
}
- void report(int state, long deltaTimeMs, int deltaBatteryUa) {
+ void report(int state, long deltaTimeMs, int deltaBatteryUa, int deltaPercent) {
final String suffix = getCounterSuffix(state);
mMetricsLogger.count(COUNTER_POWER_MILLIAMPS_PREFIX + suffix, deltaBatteryUa / 1000);
+ mMetricsLogger.count(COUNTER_POWER_PERCENT_PREFIX + suffix, deltaPercent);
mMetricsLogger.count(COUNTER_TIME_SECONDS_PREFIX + suffix, (int) (deltaTimeMs / 1000));
}
}
diff --git a/services/core/java/com/android/server/slice/SliceFullAccessList.java b/services/core/java/com/android/server/slice/SliceFullAccessList.java
index 591e809..6f5afa2 100644
--- a/services/core/java/com/android/server/slice/SliceFullAccessList.java
+++ b/services/core/java/com/android/server/slice/SliceFullAccessList.java
@@ -16,9 +16,9 @@
import android.content.Context;
import android.content.pm.UserInfo;
+import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
-import android.util.Log;
import android.util.SparseArray;
import com.android.internal.util.XmlUtils;
@@ -63,7 +63,16 @@
pkgs.add(pkg);
}
- public void writeXml(XmlSerializer out) throws IOException {
+ public void removeGrant(String pkg, int userId) {
+ ArraySet<String> pkgs = mFullAccessPkgs.get(userId, null);
+ if (pkgs == null) {
+ pkgs = new ArraySet<>();
+ mFullAccessPkgs.put(userId, pkgs);
+ }
+ pkgs.remove(pkg);
+ }
+
+ public void writeXml(XmlSerializer out, int user) throws IOException {
out.startTag(null, TAG_LIST);
out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
@@ -71,6 +80,9 @@
for (int i = 0 ; i < N; i++) {
final int userId = mFullAccessPkgs.keyAt(i);
final ArraySet<String> pkgs = mFullAccessPkgs.valueAt(i);
+ if (user != UserHandle.USER_ALL && user != userId) {
+ continue;
+ }
out.startTag(null, TAG_USER);
out.attribute(null, ATT_USER_ID, Integer.toString(userId));
if (pkgs != null) {
@@ -79,7 +91,6 @@
out.startTag(null, TAG_PKG);
out.text(pkgs.valueAt(j));
out.endTag(null, TAG_PKG);
-
}
}
out.endTag(null, TAG_USER);
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 5db0fc0..a1def44 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -21,6 +21,7 @@
import static android.content.ContentProvider.maybeAddUserId;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.SYSTEM_UID;
import android.Manifest.permission;
import android.app.ActivityManager;
@@ -31,9 +32,11 @@
import android.app.slice.ISliceManager;
import android.app.slice.SliceManager;
import android.app.slice.SliceSpec;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
@@ -66,8 +69,9 @@
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -91,7 +95,9 @@
private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>();
private final Handler mHandler;
private final ContentObserver mObserver;
+ @GuardedBy("mSliceAccessFile")
private final AtomicFile mSliceAccessFile;
+ @GuardedBy("mAccessList")
private final SliceFullAccessList mAccessList;
public SliceManagerService(Context context) {
@@ -127,11 +133,19 @@
InputStream input = mSliceAccessFile.openRead();
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setInput(input, Encoding.UTF_8.name());
- mAccessList.readXml(parser);
+ synchronized (mAccessList) {
+ mAccessList.readXml(parser);
+ }
} catch (IOException | XmlPullParserException e) {
Slog.d(TAG, "Can't read slice access file", e);
}
}
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
}
/// ----- Lifecycle stuff -----
@@ -223,7 +237,9 @@
getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS,
"Slice granting requires MANAGE_SLICE_PERMISSIONS");
if (allSlices) {
- mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
+ synchronized (mAccessList) {
+ mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
+ }
mHandler.post(mSaveAccessList);
} else {
synchronized (mLock) {
@@ -244,7 +260,71 @@
}
}
+ // Backup/restore interface
+ @Override
+ public byte[] getBackupPayload(int user) {
+ if (Binder.getCallingUid() != SYSTEM_UID) {
+ throw new SecurityException("Caller must be system");
+ }
+ //TODO: http://b/22388012
+ if (user != UserHandle.USER_SYSTEM) {
+ Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
+ return null;
+ }
+ synchronized(mSliceAccessFile) {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
+ out.setOutput(baos, Encoding.UTF_8.name());
+ synchronized (mAccessList) {
+ mAccessList.writeXml(out, user);
+ }
+ out.flush();
+ return baos.toByteArray();
+ } catch (IOException | XmlPullParserException e) {
+ Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void applyRestore(byte[] payload, int user) {
+ if (Binder.getCallingUid() != SYSTEM_UID) {
+ throw new SecurityException("Caller must be system");
+ }
+ if (payload == null) {
+ Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
+ return;
+ }
+ //TODO: http://b/22388012
+ if (user != UserHandle.USER_SYSTEM) {
+ Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
+ return;
+ }
+ synchronized(mSliceAccessFile) {
+ final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
+ try {
+ XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+ parser.setInput(bais, Encoding.UTF_8.name());
+ synchronized (mAccessList) {
+ mAccessList.readXml(parser);
+ }
+ mHandler.post(mSaveAccessList);
+ } catch (NumberFormatException | XmlPullParserException | IOException e) {
+ Slog.w(TAG, "applyRestore: error reading payload", e);
+ }
+ }
+ }
+
/// ----- internal code -----
+ private void removeFullAccess(String pkg, int userId) {
+ synchronized (mAccessList) {
+ mAccessList.removeGrant(pkg, userId);
+ }
+ mHandler.post(mSaveAccessList);
+ }
+
protected void removePinnedSlice(Uri uri) {
synchronized (mLock) {
mPinnedSlicesByUri.remove(uri).destroy();
@@ -444,7 +524,9 @@
}
private boolean isGrantedFullAccess(String pkg, int userId) {
- return mAccessList.hasFullAccess(pkg, userId);
+ synchronized (mAccessList) {
+ return mAccessList.hasFullAccess(pkg, userId);
+ }
}
private static ServiceThread createHandler() {
@@ -469,7 +551,9 @@
try {
XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
out.setOutput(stream, Encoding.UTF_8.name());
- mAccessList.writeXml(out);
+ synchronized (mAccessList) {
+ mAccessList.writeXml(out, UserHandle.USER_ALL);
+ }
out.flush();
mSliceAccessFile.finishWrite(stream);
} catch (IOException | XmlPullParserException e) {
@@ -480,6 +564,35 @@
}
};
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
+ return;
+ }
+ Uri data = intent.getData();
+ String pkg = data != null ? data.getSchemeSpecificPart() : null;
+ if (pkg == null) {
+ Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
+ return;
+ }
+ switch (intent.getAction()) {
+ case Intent.ACTION_PACKAGE_REMOVED:
+ final boolean replacing =
+ intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ if (!replacing) {
+ removeFullAccess(pkg, userId);
+ }
+ break;
+ case Intent.ACTION_PACKAGE_DATA_CLEARED:
+ removeFullAccess(pkg, userId);
+ break;
+ }
+ }
+ };
+
public static class Lifecycle extends SystemService {
private SliceManagerService mService;
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index 3f32079..b00e595 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -72,20 +72,23 @@
}
/** Cancels the notification */
- void cancel() {
+ void cancel(boolean deleteChannel) {
// We can't call into NotificationManager with WM lock held since it might call into AM.
// So, we post a message to do it later.
- mService.mH.post(this::onCancelNotification);
+ mService.mH.post(() -> onCancelNotification(deleteChannel));
}
/** Don't call with the window manager lock held! */
- private void onCancelNotification() {
+ private void onCancelNotification(boolean deleteChannel) {
if (!mPosted) {
// Notification isn't currently posted...
return;
}
mPosted = false;
mNotificationManager.cancel(mNotificationTag, NOTIFICATION_ID);
+ if (deleteChannel) {
+ mNotificationManager.deleteNotificationChannel(mNotificationTag);
+ }
}
/** Don't call with the window manager lock held! */
@@ -146,8 +149,12 @@
final String nameChannel =
context.getString(R.string.alert_windows_notification_channel_name, appName);
- final NotificationChannel channel =
- new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN);
+
+ NotificationChannel channel = mNotificationManager.getNotificationChannel(mNotificationTag);
+ if (channel != null) {
+ return;
+ }
+ channel = new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN);
channel.enableLights(false);
channel.enableVibration(false);
channel.setBlockableSystem(true);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index bd3f302d..2512dbd 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1542,11 +1542,11 @@
* Callback used to trigger bounds update after configuration change and get ids of stacks whose
* bounds were updated.
*/
- void updateStackBoundsAfterConfigChange(@NonNull List<Integer> changedStackList) {
+ void updateStackBoundsAfterConfigChange(@NonNull List<TaskStack> changedStackList) {
for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
final TaskStack stack = mTaskStackContainers.getChildAt(i);
if (stack.updateBoundsAfterConfigChange()) {
- changedStackList.add(stack.mStackId);
+ changedStackList.add(stack);
}
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 9251993..5bc739e 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -96,13 +96,17 @@
// Scale the timeout with the animator scale the controlling app is using.
mHandler.postDelayed(mTimeoutRunnable,
(long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
- try {
- mRemoteAnimationAdapter.getRunner().onAnimationStart(createAnimations(),
- mFinishedCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to start remote animation", e);
- onAnimationFinished();
- }
+
+ final RemoteAnimationTarget[] animations = createAnimations();
+ mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
+ try {
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
+ mFinishedCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to start remote animation", e);
+ onAnimationFinished();
+ }
+ });
}
private RemoteAnimationTarget[] createAnimations() {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 68c8995..8d1a822 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -47,6 +47,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -126,7 +127,8 @@
boolean mOrientationChangeComplete = true;
boolean mWallpaperActionPending = false;
- private final ArrayList<Integer> mChangedStackList = new ArrayList();
+ private final ArrayList<TaskStack> mTmpStackList = new ArrayList();
+ private final ArrayList<Integer> mTmpStackIds = new ArrayList<>();
// State for the RemoteSurfaceTrace system used in testing. If this is enabled SurfaceControl
// instances will be replaced with an instance that writes a binary representation of all
@@ -333,7 +335,8 @@
/**
* Set new display override config and return array of ids of stacks that were changed during
- * update. If called for the default display, global configuration will also be updated.
+ * update. If called for the default display, global configuration will also be updated. Stacks
+ * that are marked for deferred removal are excluded from the returned array.
*/
int[] setDisplayOverrideConfigurationIfNeeded(Configuration newConfiguration, int displayId) {
final DisplayContent displayContent = getDisplayContent(displayId);
@@ -346,24 +349,42 @@
if (!configChanged) {
return null;
}
+
displayContent.onOverrideConfigurationChanged(newConfiguration);
+ mTmpStackList.clear();
if (displayId == DEFAULT_DISPLAY) {
// Override configuration of the default display duplicates global config. In this case
// we also want to update the global config.
- return setGlobalConfigurationIfNeeded(newConfiguration);
+ setGlobalConfigurationIfNeeded(newConfiguration, mTmpStackList);
} else {
- return updateStackBoundsAfterConfigChange(displayId);
+ updateStackBoundsAfterConfigChange(displayId, mTmpStackList);
}
+
+ mTmpStackIds.clear();
+ final int stackCount = mTmpStackList.size();
+
+ for (int i = 0; i < stackCount; ++i) {
+ final TaskStack stack = mTmpStackList.get(i);
+
+ // We only include stacks that are not marked for removal as they do not exist outside
+ // of WindowManager at this point.
+ if (!stack.mDeferRemoval) {
+ mTmpStackIds.add(stack.mStackId);
+ }
+ }
+
+ return mTmpStackIds.isEmpty() ? null : ArrayUtils.convertToIntArray(mTmpStackIds);
}
- private int[] setGlobalConfigurationIfNeeded(Configuration newConfiguration) {
+ private void setGlobalConfigurationIfNeeded(Configuration newConfiguration,
+ List<TaskStack> changedStacks) {
final boolean configChanged = getConfiguration().diff(newConfiguration) != 0;
if (!configChanged) {
- return null;
+ return;
}
onConfigurationChanged(newConfiguration);
- return updateStackBoundsAfterConfigChange();
+ updateStackBoundsAfterConfigChange(changedStacks);
}
@Override
@@ -378,26 +399,18 @@
* Callback used to trigger bounds update after configuration change and get ids of stacks whose
* bounds were updated.
*/
- private int[] updateStackBoundsAfterConfigChange() {
- mChangedStackList.clear();
-
+ private void updateStackBoundsAfterConfigChange(List<TaskStack> changedStacks) {
final int numDisplays = mChildren.size();
for (int i = 0; i < numDisplays; ++i) {
final DisplayContent dc = mChildren.get(i);
- dc.updateStackBoundsAfterConfigChange(mChangedStackList);
+ dc.updateStackBoundsAfterConfigChange(changedStacks);
}
-
- return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
}
/** Same as {@link #updateStackBoundsAfterConfigChange()} but only for a specific display. */
- private int[] updateStackBoundsAfterConfigChange(int displayId) {
- mChangedStackList.clear();
-
+ private void updateStackBoundsAfterConfigChange(int displayId, List<TaskStack> changedStacks) {
final DisplayContent dc = getDisplayContent(displayId);
- dc.updateStackBoundsAfterConfigChange(mChangedStackList);
-
- return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
+ dc.updateStackBoundsAfterConfigChange(changedStacks);
}
private void prepareFreezingTaskBounds() {
@@ -599,6 +612,8 @@
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
+ mService.mAnimator.executeAfterPrepareSurfacesRunnables();
+
final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
// If we are ready to perform an app transition, check through all of the app tokens to be
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 04ae38e..f09a294 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -547,7 +547,7 @@
if (allowed) {
mAlertWindowNotification.post();
} else {
- mAlertWindowNotification.cancel();
+ mAlertWindowNotification.cancel(false /* deleteChannel */);
}
}
}
@@ -586,7 +586,7 @@
if (mAlertWindowNotification == null) {
return;
}
- mAlertWindowNotification.cancel();
+ mAlertWindowNotification.cancel(true /* deleteChannel */);
mAlertWindowNotification = null;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index cec13ab..b0d42f2 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -92,6 +92,7 @@
* executed and the corresponding transaction is closed and applied.
*/
private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
+ private boolean mInExecuteAfterPrepareSurfacesRunnables;
WindowAnimator(final WindowManagerService service) {
mService = service;
@@ -438,7 +439,13 @@
scheduleAnimation();
}
- private void executeAfterPrepareSurfacesRunnables() {
+ void executeAfterPrepareSurfacesRunnables() {
+
+ // Don't even think about to start recursing!
+ if (mInExecuteAfterPrepareSurfacesRunnables) {
+ return;
+ }
+ mInExecuteAfterPrepareSurfacesRunnables = true;
// Traverse in order they were added.
final int size = mAfterPrepareSurfacesRunnables.size();
@@ -446,5 +453,6 @@
mAfterPrepareSurfacesRunnables.get(i).run();
}
mAfterPrepareSurfacesRunnables.clear();
+ mInExecuteAfterPrepareSurfacesRunnables = false;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 066e4e6..d565a6a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -24,8 +24,6 @@
import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_USER_HANDLE;
@@ -125,7 +123,6 @@
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IAssistDataReceiver;
-import android.app.WindowConfiguration;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -2466,6 +2463,7 @@
mWaitingForConfig = false;
mLastFinishedFreezeSource = "new-config";
}
+
return mRoot.setDisplayOverrideConfigurationIfNeeded(overrideConfig, displayId);
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 210fd47..d5ed98e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -723,21 +723,9 @@
MmsServiceBroker mmsService = null;
HardwarePropertiesManagerService hardwarePropertiesService = null;
- boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false);
boolean disableRtt = SystemProperties.getBoolean("config.disable_rtt", false);
- boolean disableMediaProjection = SystemProperties.getBoolean("config.disable_mediaproj",
- false);
- boolean disableSerial = SystemProperties.getBoolean("config.disable_serial", false);
- boolean disableSearchManager = SystemProperties.getBoolean("config.disable_searchmanager",
- false);
- boolean disableTrustManager = SystemProperties.getBoolean("config.disable_trustmanager",
- false);
- boolean disableTextServices = SystemProperties.getBoolean("config.disable_textservices",
- false);
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
"config.disable_systemtextclassifier", false);
- boolean disableConsumerIr = SystemProperties.getBoolean("config.disable_consumerir", false);
- boolean disableVrManager = SystemProperties.getBoolean("config.disable_vrmanager", false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
false);
boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false);
@@ -745,6 +733,9 @@
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
+ boolean isWatch = context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WATCH);
+
// For debugging RescueParty
if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_system", false)) {
throw new RuntimeException();
@@ -819,7 +810,7 @@
ServiceManager.addService("vibrator", vibrator);
traceEnd();
- if (!disableConsumerIr) {
+ if (!isWatch) {
traceBeginAndSlog("StartConsumerIrService");
consumerIr = new ConsumerIrService(context);
ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr);
@@ -862,7 +853,7 @@
traceLog.traceEnd();
}, START_HIDL_SERVICES);
- if (!disableVrManager) {
+ if (!isWatch) {
traceBeginAndSlog("StartVrManagerService");
mSystemServiceManager.startService(VrManagerService.class);
traceEnd();
@@ -1030,7 +1021,7 @@
mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
traceEnd();
- if (!disableSystemUI) {
+ if (!isWatch) {
traceBeginAndSlog("StartStatusBarManagerService");
try {
statusBar = new StatusBarManagerService(context, wm);
@@ -1063,11 +1054,9 @@
}
traceEnd();
- if (!disableTextServices) {
- traceBeginAndSlog("StartTextServicesManager");
- mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
- traceEnd();
- }
+ traceBeginAndSlog("StartTextServicesManager");
+ mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
+ traceEnd();
if (!disableSystemTextClassifier) {
traceBeginAndSlog("StartTextClassificationManagerService");
@@ -1229,7 +1218,7 @@
}
traceEnd();
- if (!disableSearchManager) {
+ if (!isWatch) {
traceBeginAndSlog("StartSearchManagerService");
try {
mSystemServiceManager.startService(SEARCH_MANAGER_SERVICE_CLASS);
@@ -1259,7 +1248,7 @@
mSystemServiceManager.startService(DockObserver.class);
traceEnd();
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ if (isWatch) {
traceBeginAndSlog("StartThermalObserver");
mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS);
traceEnd();
@@ -1291,7 +1280,7 @@
traceEnd();
}
- if (!disableSerial) {
+ if (!isWatch) {
traceBeginAndSlog("StartSerialService");
try {
// Serial port support
@@ -1331,11 +1320,9 @@
mSystemServiceManager.startService(SoundTriggerService.class);
traceEnd();
- if (!disableTrustManager) {
- traceBeginAndSlog("StartTrustManager");
- mSystemServiceManager.startService(TrustManagerService.class);
- traceEnd();
- }
+ traceBeginAndSlog("StartTrustManager");
+ mSystemServiceManager.startService(TrustManagerService.class);
+ traceEnd();
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
traceBeginAndSlog("StartBackupManager");
@@ -1392,14 +1379,16 @@
traceEnd();
}
- traceBeginAndSlog("StartNetworkTimeUpdateService");
- try {
- networkTimeUpdater = new NetworkTimeUpdateService(context);
- ServiceManager.addService("network_time_update_service", networkTimeUpdater);
- } catch (Throwable e) {
- reportWtf("starting NetworkTimeUpdate service", e);
+ if (!isWatch) {
+ traceBeginAndSlog("StartNetworkTimeUpdateService");
+ try {
+ networkTimeUpdater = new NetworkTimeUpdateService(context);
+ ServiceManager.addService("network_time_update_service", networkTimeUpdater);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkTimeUpdate service", e);
+ }
+ traceEnd();
}
- traceEnd();
traceBeginAndSlog("StartCommonTimeManagementService");
try {
@@ -1535,13 +1524,13 @@
traceEnd();
}
- if (!disableMediaProjection) {
+ if (!isWatch) {
traceBeginAndSlog("StartMediaProjectionManager");
mSystemServiceManager.startService(MediaProjectionManagerService.class);
traceEnd();
}
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ if (isWatch) {
traceBeginAndSlog("StartWearConnectivityService");
mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS);
traceEnd();
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 c863aab..473a813 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
@@ -39,6 +39,7 @@
import android.os.Binder;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
+import android.security.KeyStore;
import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
@@ -49,6 +50,7 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
+import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
@@ -135,6 +137,8 @@
@Mock private RecoverySnapshotListenersStorage mMockListenersStorage;
@Mock private KeyguardManager mKeyguardManager;
@Mock private PlatformKeyManager mPlatformKeyManager;
+ @Mock private KeyStore mKeyStore;
+ @Mock private ApplicationKeyStorage mApplicationKeyStorage;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private File mDatabaseFile;
@@ -164,12 +168,14 @@
mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
mMockContext,
+ mKeyStore,
mRecoverableKeyStoreDb,
mRecoverySessionStorage,
Executors.newSingleThreadExecutor(),
mRecoverySnapshotStorage,
mMockListenersStorage,
- mPlatformKeyManager);
+ mPlatformKeyManager,
+ mApplicationKeyStorage);
}
@After
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 56d4b7e..857925b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -5149,7 +5149,8 @@
.forAllShortcuts(si -> {
switch (package1DisabledReason) {
case ShortcutInfo.DISABLED_REASON_VERSION_LOWER:
- assertEquals("This shortcut requires latest app",
+ assertEquals("App version downgraded, or isn’t compatible"
+ + " with this shortcut",
si.getDisabledMessage());
break;
case ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH:
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index c2072df..f559986a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -53,6 +53,7 @@
assertFalse(opt.isDowngrade());
assertFalse(opt.isForce());
assertFalse(opt.isDexoptIdleBackgroundJob());
+ assertFalse(opt.isDexoptInstallWithDexMetadata());
}
@Test
@@ -65,7 +66,8 @@
DexoptOptions.DEXOPT_ONLY_SHARED_DEX |
DexoptOptions.DEXOPT_DOWNGRADE |
DexoptOptions.DEXOPT_AS_SHARED_LIBRARY |
- DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
+ DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB |
+ DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags);
assertEquals(mPackageName, opt.getPackageName());
@@ -79,6 +81,7 @@
assertTrue(opt.isForce());
assertTrue(opt.isDexoptAsSharedLibrary());
assertTrue(opt.isDexoptIdleBackgroundJob());
+ assertTrue(opt.isDexoptInstallWithDexMetadata());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
index c3714c8..f7516b2 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
@@ -63,6 +63,11 @@
return mBatteryLevel;
}
+ @Override
+ int injectBatteryPercent() {
+ return mBatteryLevel / 10;
+ }
+
void assertDumpable() {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
dump(new PrintWriter(out), ""); // Just make sure it won't crash.
@@ -102,7 +107,7 @@
target.assertDumpable();
target.advanceClock(1);
- target.drainBattery(2);
+ target.drainBattery(200);
target.transitionState(
BatterySaverState.OFF,
@@ -110,7 +115,7 @@
DozeState.NOT_DOZING);
target.advanceClock(4);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -118,7 +123,7 @@
DozeState.NOT_DOZING);
target.advanceClock(2);
- target.drainBattery(5);
+ target.drainBattery(500);
target.transitionState(
BatterySaverState.OFF,
@@ -126,7 +131,7 @@
DozeState.NOT_DOZING);
target.advanceClock(4);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -134,7 +139,7 @@
DozeState.NOT_DOZING);
target.advanceClock(2);
- target.drainBattery(5);
+ target.drainBattery(500);
target.transitionState(
BatterySaverState.OFF,
@@ -142,7 +147,7 @@
DozeState.NOT_DOZING);
target.advanceClock(3);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -150,7 +155,7 @@
DozeState.LIGHT);
target.advanceClock(5);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -158,7 +163,7 @@
DozeState.DEEP);
target.advanceClock(1);
- target.drainBattery(2);
+ target.drainBattery(200);
target.transitionState(
BatterySaverState.ON,
@@ -166,7 +171,7 @@
DozeState.NOT_DOZING);
target.advanceClock(1);
- target.drainBattery(3);
+ target.drainBattery(300);
target.transitionState(
BatterySaverState.OFF,
@@ -174,7 +179,7 @@
DozeState.NOT_DOZING);
target.advanceClock(3);
- target.drainBattery(5);
+ target.drainBattery(500);
target.transitionState(
BatterySaverState.ON,
@@ -182,12 +187,12 @@
DozeState.NOT_DOZING);
target.advanceClock(3);
- target.drainBattery(5);
+ target.drainBattery(500);
target.startCharging();
target.advanceClock(5);
- target.drainBattery(10);
+ target.drainBattery(1000);
target.transitionState(
BatterySaverState.ON,
@@ -195,25 +200,25 @@
DozeState.NOT_DOZING);
target.advanceClock(5);
- target.drainBattery(1);
+ target.drainBattery(100);
target.startCharging();
target.assertDumpable();
assertEquals(
- "BS=0,I=0,D=0:{4m,10,150.00}\n" +
- "BS=1,I=0,D=0:{0m,0,0.00}\n" +
- "BS=0,I=1,D=0:{14m,8,34.29}\n" +
- "BS=1,I=1,D=0:{9m,9,60.00}\n" +
- "BS=0,I=0,D=1:{5m,1,12.00}\n" +
- "BS=1,I=0,D=1:{0m,0,0.00}\n" +
- "BS=0,I=1,D=1:{0m,0,0.00}\n" +
- "BS=1,I=1,D=1:{0m,0,0.00}\n" +
- "BS=0,I=0,D=2:{1m,2,120.00}\n" +
- "BS=1,I=0,D=2:{0m,0,0.00}\n" +
- "BS=0,I=1,D=2:{0m,0,0.00}\n" +
- "BS=1,I=1,D=2:{0m,0,0.00}",
+ "BS=0,I=0,D=0:{4m,1000,15000.00uA/H,1500.00%}\n" +
+ "BS=1,I=0,D=0:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=1,D=0:{14m,800,3428.57uA/H,342.86%}\n" +
+ "BS=1,I=1,D=0:{9m,900,6000.00uA/H,600.00%}\n" +
+ "BS=0,I=0,D=1:{5m,100,1200.00uA/H,120.00%}\n" +
+ "BS=1,I=0,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=1,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=0,D=2:{1m,200,12000.00uA/H,1200.00%}\n" +
+ "BS=1,I=0,D=2:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=1,D=2:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=1,I=1,D=2:{0m,0,0.00uA/H,0.00%}",
target.toDebugString());
}
@@ -245,6 +250,7 @@
DozeState.NOT_DOZING);
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "01", 2);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "01", 200);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "01", 60);
target.advanceClock(1);
@@ -277,15 +283,17 @@
DozeState.NOT_DOZING);
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "00", 2 * 3);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "00", 200 * 3);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "00", 60 * 3);
target.advanceClock(10);
- target.drainBattery(10_000);
+ target.drainBattery(10000);
reset(mMetricsLogger);
target.startCharging();
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "11", 10);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "11", 1000);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "11", 60 * 10);
target.advanceClock(1);
@@ -305,6 +313,7 @@
target.startCharging();
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "10", 2);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "10", 200);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "10", 60);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
new file mode 100644
index 0000000..51b019a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -0,0 +1,50 @@
+package com.android.server.wm;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the {@link RootWindowContainer} class.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:com.android.server.wm.RootWindowContainerTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RootWindowContainerTests extends WindowTestsBase {
+ @Test
+ public void testSetDisplayOverrideConfigurationIfNeeded() throws Exception {
+ // Add first stack we expect to be updated with configuration change.
+ final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
+ stack.getOverrideConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 5, 5));
+
+ // Add second task that will be set for deferred removal that should not be returned
+ // with the configuration change.
+ final TaskStack deferredDeletedStack = createTaskStackOnDisplay(mDisplayContent);
+ deferredDeletedStack.getOverrideConfiguration().windowConfiguration.setBounds(
+ new Rect(0, 0, 5, 5));
+ deferredDeletedStack.mDeferRemoval = true;
+
+ final Configuration override = new Configuration(
+ mDisplayContent.getOverrideConfiguration());
+ override.windowConfiguration.setBounds(new Rect(0, 0, 10, 10));
+
+ // Set display override.
+ final int[] results = sWm.mRoot.setDisplayOverrideConfigurationIfNeeded(override,
+ mDisplayContent.getDisplayId());
+
+ // Ensure only first stack is returned.
+ assertTrue(results.length == 1);
+ assertTrue(results[0] == stack.mStackId);
+ }
+}
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 99eb846..e36586e 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -210,6 +210,9 @@
runCommand(instrumentation, "cmd package set-home-activity --user "
+ instrumentation.getContext().getUserId() + " " + component,
result -> result.contains("Success"));
+ runCommand(instrumentation, "cmd shortcut clear-default-launcher --user "
+ + instrumentation.getContext().getUserId(),
+ result -> result.contains("Success"));
}
public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9ae6f00..6b6df29 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -117,7 +117,7 @@
public class NotificationManagerServiceTest extends UiServiceTestCase {
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
private final int mUid = Binder.getCallingUid();
- private NotificationManagerService mService;
+ private TestableNotificationManagerService mService;
private INotificationManager mBinderService;
private NotificationManagerInternal mInternalService;
@Mock
@@ -152,17 +152,21 @@
// Use a Testable subclass so we can simulate calls from the system without failing.
private static class TestableNotificationManagerService extends NotificationManagerService {
+ int countSystemChecks = 0;
+
public TestableNotificationManagerService(Context context) {
super(context);
}
@Override
protected boolean isCallingUidSystem() {
+ countSystemChecks++;
return true;
}
@Override
protected boolean isCallerSystemOrPhone() {
+ countSystemChecks++;
return true;
}
@@ -2429,4 +2433,18 @@
.setUid(user2.sbn.getUid())
.setLastNotified(user2.sbn.getPostTime())));
}
+
+ @Test
+ public void testRestore() throws Exception {
+ int systemChecks = mService.countSystemChecks;
+ mBinderService.applyRestore(null, UserHandle.USER_SYSTEM);
+ assertEquals(1, mService.countSystemChecks - systemChecks);
+ }
+
+ @Test
+ public void testBackup() throws Exception {
+ int systemChecks = mService.countSystemChecks;
+ mBinderService.getBackupPayload(1);
+ assertEquals(1, mService.countSystemChecks - systemChecks);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
index 7c14d08..bc28150 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.util.Xml.Encoding;
@@ -67,6 +68,15 @@
}
@Test
+ public void testRemoveAccess() {
+ mAccessList.grantFullAccess("pkg", 0);
+ assertTrue(mAccessList.hasFullAccess("pkg", 0));
+
+ mAccessList.removeGrant("pkg", 0);
+ assertFalse(mAccessList.hasFullAccess("pkg", 0));
+ }
+
+ @Test
public void testSerialization() throws XmlPullParserException, IOException {
mAccessList.grantFullAccess("pkg", 0);
mAccessList.grantFullAccess("pkg1", 0);
@@ -76,7 +86,7 @@
ByteArrayOutputStream output = new ByteArrayOutputStream();
XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
out.setOutput(output, Encoding.UTF_8.name());
- mAccessList.writeXml(out);
+ mAccessList.writeXml(out, UserHandle.USER_ALL);
out.flush();
assertEquals(TEST_XML, output.toString(Encoding.UTF_8.name()));
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index fcfc593..95eb14a 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -134,6 +134,25 @@
"android.telecom.extra.LOG_SELF_MANAGED_CALLS";
/**
+ * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
+ * indicates whether calls for a {@link PhoneAccount} should generate a "call recording tone"
+ * when the user is recording audio on the device.
+ * <p>
+ * The call recording tone is played over the telephony audio stream so that the remote party
+ * has an audible indication that it is possible their call is being recorded using a call
+ * recording app on the device.
+ * <p>
+ * This extra only has an effect for calls placed via Telephony (e.g.
+ * {@link #CAPABILITY_SIM_SUBSCRIPTION}).
+ * <p>
+ * The call recording tone is a 1400 hz tone which repeats every 15 seconds while recording is
+ * in progress.
+ * @hide
+ */
+ public static final String EXTRA_PLAY_CALL_RECORDING_TONE =
+ "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
+
+ /**
* Flag indicating that this {@code PhoneAccount} can act as a connection manager for
* other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
* will be allowed to manage phone calls including using its own proprietary phone-call
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a34e9f9..63ab766 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -77,6 +77,14 @@
public static final String
KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
+ /**
+ * Boolean indicating if the "Call barring" item is visible in the Call Settings menu.
+ * true means visible. false means gone.
+ * @hide
+ */
+ public static final String KEY_CALL_BARRING_VISIBILITY_BOOL =
+ "call_barring_visibility_bool";
+
/**
* Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
* events from the Sim.
@@ -146,6 +154,15 @@
public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
/**
+ * Determines if the carrier requires that a tone be played to the remote party when an app is
+ * recording audio during a call (e.g. using a call recording app).
+ * <p>
+ * Note: This requires the Telephony config_supports_telephony_audio_device overlay to be true
+ * in order to work.
+ * @hide
+ */
+ public static final String KEY_PLAY_CALL_RECORDING_TONE_BOOL = "play_call_recording_tone_bool";
+ /**
* Determines if the carrier requires converting the destination number before sending out an
* SMS. Certain networks and numbering plans require different formats.
*/
@@ -1381,6 +1398,14 @@
public static final String KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO = "video_calls_can_be_hd_audio";
/**
+ * When true, indicates that the HD audio icon in the in-call screen should be shown for
+ * GSM/CDMA calls.
+ * @hide
+ */
+ public static final String KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO =
+ "gsm_cdma_calls_can_be_hd_audio";
+
+ /**
* Whether system apps are allowed to use fallback if carrier video call is not available.
* Defaults to {@code true}.
*
@@ -1793,6 +1818,7 @@
sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_LOCAL_DTMF_TONES_BOOL, true);
+ sDefaults.putBoolean(KEY_PLAY_CALL_RECORDING_TONE_BOOL, false);
sDefaults.putBoolean(KEY_APN_EXPAND_BOOL, true);
sDefaults.putBoolean(KEY_AUTO_RETRY_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
@@ -1833,6 +1859,7 @@
sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false);
+ sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false);
sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
@@ -2033,6 +2060,7 @@
sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL, true);
sDefaults.putBoolean(KEY_WIFI_CALLS_CAN_BE_HD_AUDIO, true);
sDefaults.putBoolean(KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO, true);
+ sDefaults.putBoolean(KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO, false);
sDefaults.putBoolean(KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL, true);
sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index dfaaab9..ece1ee3 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -36,48 +36,14 @@
private int mEvdoEcio; // This value is the EVDO Ec/Io
private int mEvdoSnr; // Valid values are 0-8. 8 is the highest signal to noise ratio
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthCdma() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthCdma(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio,
int evdoSnr) {
- initialize(cdmaDbm, cdmaEcio, evdoDbm, evdoEcio, evdoSnr);
- }
-
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthCdma(CellSignalStrengthCdma s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param cdmaDbm
- * @param cdmaEcio
- * @param evdoDbm
- * @param evdoEcio
- * @param evdoSnr
- *
- * @hide
- */
- public void initialize(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio, int evdoSnr) {
mCdmaDbm = cdmaDbm;
mCdmaEcio = cdmaEcio;
mEvdoDbm = evdoDbm;
@@ -85,9 +51,12 @@
mEvdoSnr = evdoSnr;
}
- /**
- * @hide
- */
+ /** @hide */
+ public CellSignalStrengthCdma(CellSignalStrengthCdma s) {
+ copyFrom(s);
+ }
+
+ /** @hide */
protected void copyFrom(CellSignalStrengthCdma s) {
mCdmaDbm = s.mCdmaDbm;
mCdmaEcio = s.mCdmaEcio;
@@ -96,9 +65,7 @@
mEvdoSnr = s.mEvdoSnr;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthCdma copy() {
return new CellSignalStrengthCdma(this);
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index f68d2ca..8687cd1 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -34,80 +34,40 @@
private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
private static final int GSM_SIGNAL_STRENGTH_MODERATE = 5;
- private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
+ private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
- private int mTimingAdvance;
+ private int mTimingAdvance; // range from 0-219 or Integer.MAX_VALUE if unknown
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthGsm() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthGsm(int ss, int ber) {
- initialize(ss, ber);
+ this(ss, ber, Integer.MAX_VALUE);
}
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param ss SignalStrength as ASU value
- * @param ber is Bit Error Rate
- *
- * @hide
- */
- public void initialize(int ss, int ber) {
- mSignalStrength = ss;
- mBitErrorRate = ber;
- mTimingAdvance = Integer.MAX_VALUE;
- }
-
- /**
- * Initialize all the values
- *
- * @param ss SignalStrength as ASU value
- * @param ber is Bit Error Rate
- * @param ta timing advance
- *
- * @hide
- */
- public void initialize(int ss, int ber, int ta) {
+ /** @hide */
+ public CellSignalStrengthGsm(int ss, int ber, int ta) {
mSignalStrength = ss;
mBitErrorRate = ber;
mTimingAdvance = ta;
}
- /**
- * @hide
- */
+ /** @hide */
+ public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
+ copyFrom(s);
+ }
+
+ /** @hide */
protected void copyFrom(CellSignalStrengthGsm s) {
mSignalStrength = s.mSignalStrength;
mBitErrorRate = s.mBitErrorRate;
mTimingAdvance = s.mTimingAdvance;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthGsm copy() {
return new CellSignalStrengthGsm(this);
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 6ffc8b6..f009fb1 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -37,50 +37,15 @@
private int mCqi;
private int mTimingAdvance;
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthLte() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthLte(int signalStrength, int rsrp, int rsrq, int rssnr, int cqi,
int timingAdvance) {
- initialize(signalStrength, rsrp, rsrq, rssnr, cqi, timingAdvance);
- }
-
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthLte(CellSignalStrengthLte s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param lteSignalStrength
- * @param rsrp
- * @param rsrq
- * @param rssnr
- * @param cqi
- *
- * @hide
- */
- public void initialize(int lteSignalStrength, int rsrp, int rsrq, int rssnr, int cqi,
- int timingAdvance) {
- mSignalStrength = lteSignalStrength;
+ mSignalStrength = signalStrength;
mRsrp = rsrp;
mRsrq = rsrq;
mRssnr = rssnr;
@@ -88,25 +53,12 @@
mTimingAdvance = timingAdvance;
}
- /**
- * Initialize from the SignalStrength structure.
- *
- * @param ss
- *
- * @hide
- */
- public void initialize(SignalStrength ss, int timingAdvance) {
- mSignalStrength = ss.getLteSignalStrength();
- mRsrp = ss.getLteRsrp();
- mRsrq = ss.getLteRsrq();
- mRssnr = ss.getLteRssnr();
- mCqi = ss.getLteCqi();
- mTimingAdvance = timingAdvance;
+ /** @hide */
+ public CellSignalStrengthLte(CellSignalStrengthLte s) {
+ copyFrom(s);
}
- /**
- * @hide
- */
+ /** @hide */
protected void copyFrom(CellSignalStrengthLte s) {
mSignalStrength = s.mSignalStrength;
mRsrp = s.mRsrp;
@@ -116,9 +68,7 @@
mTimingAdvance = s.mTimingAdvance;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthLte copy() {
return new CellSignalStrengthLte(this);
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 2cd56b8..dd32a96 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -34,62 +34,32 @@
private static final int WCDMA_SIGNAL_STRENGTH_GOOD = 8;
private static final int WCDMA_SIGNAL_STRENGTH_MODERATE = 5;
- private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
- private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+ private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
+ private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthWcdma() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthWcdma(int ss, int ber) {
- initialize(ss, ber);
- }
-
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param ss SignalStrength as ASU value
- * @param ber is Bit Error Rate
- *
- * @hide
- */
- public void initialize(int ss, int ber) {
mSignalStrength = ss;
mBitErrorRate = ber;
}
- /**
- * @hide
- */
+ /** @hide */
+ public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
+ copyFrom(s);
+ }
+
+ /** @hide */
protected void copyFrom(CellSignalStrengthWcdma s) {
mSignalStrength = s.mSignalStrength;
mBitErrorRate = s.mBitErrorRate;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthWcdma copy() {
return new CellSignalStrengthWcdma(this);
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
new file mode 100644
index 0000000..b362df9
--- /dev/null
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -0,0 +1,160 @@
+/*
+ * 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
+ */
+
+package android.telephony;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.location.LocationManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Process;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.SparseBooleanArray;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper for performing location access checks.
+ * @hide
+ */
+public final class LocationAccessPolicy {
+ /**
+ * API to determine if the caller has permissions to get cell location.
+ *
+ * @param pkgName Package name of the application requesting access
+ * @param uid The uid of the package
+ * @param pid The pid of the package
+ * @return boolean true or false if permissions is granted
+ */
+ public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
+ int uid, int pid) throws SecurityException {
+ Trace.beginSection("TelephonyLocationCheck");
+ try {
+ // Always allow the phone process to access location. This avoid breaking legacy code
+ // that rely on public-facing APIs to access cell location, and it doesn't create a
+ // info leak risk because the cell location is stored in the phone process anyway.
+ if (uid == Process.PHONE_UID) {
+ return true;
+ }
+
+ // We always require the location permission and also require the
+ // location mode to be on for non-legacy apps. Legacy apps are
+ // required to be in the foreground to at least mitigate the case
+ // where a legacy app the user is not using tracks their location.
+ // Granting ACCESS_FINE_LOCATION to an app automatically grants it
+ // ACCESS_COARSE_LOCATION.
+
+ if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid) ==
+ PackageManager.PERMISSION_DENIED) {
+ return false;
+ }
+ final int opCode = AppOpsManager.permissionToOpCode(
+ Manifest.permission.ACCESS_COARSE_LOCATION);
+ if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
+ .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))
+ && !isLegacyForeground(context, pkgName, uid)) {
+ return false;
+ }
+ // If the user or profile is current, permission is granted.
+ // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+ return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
+ int locationMode = Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId);
+ return locationMode != Settings.Secure.LOCATION_MODE_OFF
+ && locationMode != Settings.Secure.LOCATION_MODE_SENSORS_ONLY;
+ }
+
+ private static boolean isLegacyForeground(@NonNull Context context, @NonNull String pkgName,
+ int uid) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ return isLegacyVersion(context, pkgName) && isForegroundApp(context, uid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private static boolean isLegacyVersion(@NonNull Context context, @NonNull String pkgName) {
+ try {
+ if (context.getPackageManager().getApplicationInfo(pkgName, 0)
+ .targetSdkVersion <= Build.VERSION_CODES.O) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // In case of exception, assume known app (more strict checking)
+ // Note: This case will never happen since checkPackage is
+ // called to verify validity before checking app's version.
+ }
+ return false;
+ }
+
+ private static boolean isForegroundApp(@NonNull Context context, int uid) {
+ final ActivityManager am = context.getSystemService(ActivityManager.class);
+ return am.getUidImportance(uid) <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+ }
+
+ private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
+ return context.checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private static boolean isCurrentProfile(@NonNull Context context, int uid) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ final int currentUser = ActivityManager.getCurrentUser();
+ final int callingUserId = UserHandle.getUserId(uid);
+ if (callingUserId == currentUser) {
+ return true;
+ } else {
+ List<UserInfo> userProfiles = context.getSystemService(
+ UserManager.class).getProfiles(currentUser);
+ for (UserInfo user : userProfiles) {
+ if (user.id == callingUserId) {
+ return true;
+ }
+ }
+ }
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index debf43d..34f2dac 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -612,9 +612,9 @@
* onSubscriptionsChanged overridden.
*/
public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
- String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
if (DBG) {
- logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
+ logd("register OnSubscriptionsChangedListener pkgName=" + pkgName
+ " listener=" + listener);
}
try {
@@ -623,7 +623,7 @@
ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));
if (tr != null) {
- tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
+ tr.addOnSubscriptionsChangedListener(pkgName, listener.callback);
}
} catch (RemoteException ex) {
// Should not happen
diff --git a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
index 7145110..021e386 100644
--- a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
+++ b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
@@ -14,12 +14,16 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.perftests.amteststestapp">
+ package="com.android.frameworks.perftests.amteststestapp">
<application android:name=".TestApplication">
<activity android:name=".TestActivity" android:exported="true"/>
+ <provider
+ android:authorities="com.android.frameworks.perftests.amteststestapp"
+ android:name=".TestContentProvider"
+ android:exported="true" />
<receiver
- android:name=".TestBroadcastReceiver"
- android:exported="true">
+ android:name=".TestBroadcastReceiver"
+ android:exported="true">
<intent-filter>
<action android:name="com.android.frameworks.perftests.ACTION_BROADCAST_MANIFEST_RECEIVE" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java
new file mode 100644
index 0000000..0940578
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.amteststestapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class TestContentProvider extends ContentProvider {
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public boolean onCreate() {
+ return false;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java
new file mode 100644
index 0000000..3bf56ce
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.am.tests;
+
+import android.content.ContentProviderClient;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.perftests.am.util.TargetPackageUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ContentProviderPerfTest extends BasePerfTest {
+ /**
+ * Benchmark time to call ContentResolver.acquireContentProviderClient() when target package is
+ * running.
+ */
+ @Test
+ public void contentProviderRunning() {
+ runPerfFunction(() -> {
+ startTargetPackage();
+
+ long startTimeNs = System.nanoTime();
+ final ContentProviderClient contentProviderClient =
+ mContext.getContentResolver()
+ .acquireContentProviderClient(TargetPackageUtils.PACKAGE_NAME);
+ final long endTimeNs = System.nanoTime();
+
+ contentProviderClient.close();
+
+ return endTimeNs - startTimeNs;
+ });
+ }
+
+ /**
+ * Benchmark time to call ContentResolver.acquireContentProviderClient() when target package is
+ * not running.
+ */
+ @Test
+ public void contentProviderNotRunning() {
+ runPerfFunction(() -> {
+ final long startTimeNs = System.nanoTime();
+ final ContentProviderClient contentProviderClient =
+ mContext.getContentResolver().acquireContentProviderClient(
+ TargetPackageUtils.PACKAGE_NAME);
+ final long endTimeNs = System.nanoTime();
+
+ contentProviderClient.close();
+
+ return endTimeNs - startTimeNs;
+ });
+ }
+}
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index dc5ba0c..fe63aa1 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -7,22 +7,21 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
+import android.content.pm.PackageManager;
+import android.net.wifi.rtt.RangingRequest;
+import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.RangingResultCallback;
+import android.net.wifi.rtt.WifiRttManager;
import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.util.Log;
-import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
+import java.util.List;
+
/** @hide */
@SystemApi
@SystemService(Context.WIFI_RTT_SERVICE)
@@ -175,7 +174,8 @@
@Deprecated
@SuppressLint("Doclava125")
public Capabilities getCapabilities() {
- return new Capabilities();
+ throw new UnsupportedOperationException(
+ "getCapabilities is not supported in the adaptation layer");
}
/**
@@ -316,16 +316,7 @@
@RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
public RttCapabilities getRttCapabilities() {
- synchronized (mCapabilitiesLock) {
- if (mRttCapabilities == null) {
- try {
- mRttCapabilities = mService.getRttCapabilities();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- return mRttCapabilities;
- }
+ return mRttCapabilities;
}
/** specifies parameters for RTT request */
@@ -972,69 +963,6 @@
}
}
- private boolean rttParamSanity(RttParams params, int index) {
- if (mRttCapabilities == null) {
- if(getRttCapabilities() == null) {
- Log.e(TAG, "Can not get RTT capabilities");
- throw new IllegalStateException("RTT chip is not working");
- }
- }
-
- if (params.deviceType != RTT_PEER_TYPE_AP) {
- return false;
- } else if (params.requestType != RTT_TYPE_ONE_SIDED && params.requestType !=
- RTT_TYPE_TWO_SIDED) {
- Log.e(TAG, "Request " + index + ": Illegal Request Type: " + params.requestType);
- return false;
- } else if (params.requestType == RTT_TYPE_ONE_SIDED &&
- !mRttCapabilities.oneSidedRttSupported) {
- Log.e(TAG, "Request " + index + ": One side RTT is not supported");
- return false;
- } else if (params.requestType == RTT_TYPE_TWO_SIDED &&
- !mRttCapabilities.twoSided11McRttSupported) {
- Log.e(TAG, "Request " + index + ": two side RTT is not supported");
- return false;
- } else if(params.bssid == null || params.bssid.isEmpty()) {
- Log.e(TAG,"No BSSID in params");
- return false;
- } else if ( params.numberBurst != 0 ) {
- Log.e(TAG, "Request " + index + ": Illegal number of burst: " + params.numberBurst);
- return false;
- } else if (params.numSamplesPerBurst <= 0 || params.numSamplesPerBurst > 31) {
- Log.e(TAG, "Request " + index + ": Illegal sample number per burst: " +
- params.numSamplesPerBurst);
- return false;
- } else if (params.numRetriesPerMeasurementFrame < 0 ||
- params.numRetriesPerMeasurementFrame > 3) {
- Log.e(TAG, "Request " + index + ": Illegal measurement frame retry number:" +
- params.numRetriesPerMeasurementFrame);
- return false;
- } else if(params.numRetriesPerFTMR < 0 ||
- params.numRetriesPerFTMR > 3) {
- Log.e(TAG, "Request " + index + ": Illegal FTMR frame retry number:" +
- params.numRetriesPerFTMR);
- return false;
- } else if (params.LCIRequest && !mRttCapabilities.lciSupported) {
- Log.e(TAG, "Request " + index + ": LCI is not supported");
- return false;
- } else if (params.LCRRequest && !mRttCapabilities.lcrSupported) {
- Log.e(TAG, "Request " + index + ": LCR is not supported");
- return false;
- } else if (params.burstTimeout < 1 ||
- (params.burstTimeout > 11 && params.burstTimeout != 15)){
- Log.e(TAG, "Request " + index + ": Illegal burst timeout: " + params.burstTimeout);
- return false;
- } else if ((params.preamble & mRttCapabilities.preambleSupported) == 0) {
- Log.e(TAG, "Request " + index + ": Do not support this preamble: " + params.preamble);
- return false;
- } else if ((params.bandwidth & mRttCapabilities.bwSupported) == 0) {
- Log.e(TAG, "Request " + index + ": Do not support this bandwidth: " + params.bandwidth);
- return false;
- }
-
- return true;
- }
-
/**
* Request to start an RTT ranging
*
@@ -1045,24 +973,72 @@
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void startRanging(RttParams[] params, RttListener listener) {
- int index = 0;
- for(RttParams rttParam : params) {
- if (!rttParamSanity(rttParam, index)) {
- throw new IllegalArgumentException("RTT Request Parameter Illegal");
- }
- index++;
- }
- validateChannel();
- ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
Log.i(TAG, "Send RTT request to RTT Service");
- mAsyncChannel.sendMessage(CMD_OP_START_RANGING,
- 0, putListener(listener), parcelableParams);
+
+ if (!mNewService.isAvailable()) {
+ listener.onFailure(REASON_NOT_AVAILABLE, "");
+ return;
+ }
+
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ for (RttParams rttParams : params) {
+ if (rttParams.deviceType != RTT_PEER_TYPE_AP) {
+ listener.onFailure(REASON_INVALID_REQUEST, "Only AP peers are supported");
+ return;
+ }
+
+ ScanResult reconstructed = new ScanResult();
+ reconstructed.BSSID = rttParams.bssid;
+ if (rttParams.requestType == RTT_TYPE_TWO_SIDED) {
+ reconstructed.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+ }
+ reconstructed.channelWidth = rttParams.channelWidth;
+ reconstructed.frequency = rttParams.frequency;
+ reconstructed.centerFreq0 = rttParams.centerFreq0;
+ reconstructed.centerFreq1 = rttParams.centerFreq1;
+ builder.addResponder(
+ android.net.wifi.rtt.ResponderConfig.fromScanResult(reconstructed));
+ }
+ try {
+ mNewService.startRanging(builder.build(), new RangingResultCallback() {
+ @Override
+ public void onRangingFailure(int code) {
+ int localCode = REASON_UNSPECIFIED;
+ if (code == STATUS_CODE_FAIL_RTT_NOT_AVAILABLE) {
+ localCode = REASON_NOT_AVAILABLE;
+ }
+ listener.onFailure(localCode, "");
+ }
+
+ @Override
+ public void onRangingResults(List<RangingResult> results) {
+ RttResult[] legacyResults = new RttResult[results.size()];
+ int i = 0;
+ for (RangingResult result : results) {
+ legacyResults[i] = new RttResult();
+ legacyResults[i].status = result.getStatus();
+ legacyResults[i].bssid = result.getMacAddress().toString();
+ legacyResults[i].distance = result.getDistanceMm() / 10;
+ legacyResults[i].distanceStandardDeviation =
+ result.getDistanceStdDevMm() / 10;
+ legacyResults[i].rssi = result.getRssi();
+ legacyResults[i].ts = result.getRangingTimestampUs();
+ }
+ listener.onSuccess(legacyResults);
+ }
+ }, null);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "startRanging: invalid arguments - " + e);
+ listener.onFailure(REASON_INVALID_REQUEST, e.getMessage());
+ } catch (SecurityException e) {
+ Log.e(TAG, "startRanging: security exception - " + e);
+ listener.onFailure(REASON_PERMISSION_DENIED, e.getMessage());
+ }
}
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void stopRanging(RttListener listener) {
- validateChannel();
- mAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
+ Log.e(TAG, "stopRanging: unsupported operation - nop");
}
/**
@@ -1095,12 +1071,8 @@
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void enableResponder(ResponderCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- validateChannel();
- int key = putListenerIfAbsent(callback);
- mAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
+ throw new UnsupportedOperationException(
+ "enableResponder is not supported in the adaptation layer");
}
/**
@@ -1115,16 +1087,8 @@
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void disableResponder(ResponderCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- validateChannel();
- int key = removeListener(callback);
- if (key == INVALID_KEY) {
- Log.e(TAG, "responder not enabled yet");
- return;
- }
- mAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
+ throw new UnsupportedOperationException(
+ "disableResponder is not supported in the adaptation layer");
}
/**
@@ -1238,17 +1202,9 @@
/** @hide */
public static final int CMD_OP_REG_BINDER = BASE + 9;
- private static final int INVALID_KEY = 0;
-
private final Context mContext;
- private final IRttManager mService;
- private final SparseArray mListenerMap = new SparseArray();
- private final Object mListenerMapLock = new Object();
- private final Object mCapabilitiesLock = new Object();
-
+ private final WifiRttManager mNewService;
private RttCapabilities mRttCapabilities;
- private int mListenerKey = 1;
- private AsyncChannel mAsyncChannel;
/**
* Create a new WifiScanner instance.
@@ -1263,170 +1219,20 @@
*/
public RttManager(Context context, IRttManager service, Looper looper) {
mContext = context;
- mService = service;
- Messenger messenger = null;
- int[] key = new int[1];
- try {
- Log.d(TAG, "Get the messenger from " + mService);
- messenger = mService.getMessenger(new Binder(), key);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mNewService = (WifiRttManager) mContext.getSystemService(Context.WIFI_RTT_RANGING_SERVICE);
- if (messenger == null) {
- throw new IllegalStateException("getMessenger() returned null! This is invalid.");
- }
+ boolean rttSupported = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_RTT);
- mAsyncChannel = new AsyncChannel();
-
- Handler handler = new ServiceHandler(looper);
- mAsyncChannel.connectSync(mContext, handler, messenger);
- // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message
- // synchronously, which causes RttService to receive the wrong replyTo value.
- mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION,
- new RttClient(context.getPackageName()));
- mAsyncChannel.sendMessage(CMD_OP_REG_BINDER, key[0]);
+ mRttCapabilities = new RttCapabilities();
+ mRttCapabilities.oneSidedRttSupported = rttSupported;
+ mRttCapabilities.twoSided11McRttSupported = rttSupported;
+ mRttCapabilities.lciSupported = false;
+ mRttCapabilities.lcrSupported = false;
+ mRttCapabilities.preambleSupported = PREAMBLE_HT | PREAMBLE_VHT;
+ mRttCapabilities.bwSupported = RTT_BW_40_SUPPORT | RTT_BW_80_SUPPORT;
+ mRttCapabilities.responderSupported = false;
+ mRttCapabilities.secureRttSupported = false;
}
-
- private void validateChannel() {
- if (mAsyncChannel == null) throw new IllegalStateException(
- "No permission to access and change wifi or a bad initialization");
- }
-
- private int putListener(Object listener) {
- if (listener == null) return INVALID_KEY;
- int key;
- synchronized (mListenerMapLock) {
- do {
- key = mListenerKey++;
- } while (key == INVALID_KEY);
- mListenerMap.put(key, listener);
- }
- return key;
- }
-
- // Insert a listener if it doesn't exist in mListenerMap. Returns the key of the listener.
- private int putListenerIfAbsent(Object listener) {
- if (listener == null) return INVALID_KEY;
- synchronized (mListenerMapLock) {
- int key = getListenerKey(listener);
- if (key != INVALID_KEY) {
- return key;
- }
- do {
- key = mListenerKey++;
- } while (key == INVALID_KEY);
- mListenerMap.put(key, listener);
- return key;
- }
-
- }
-
- private Object getListener(int key) {
- if (key == INVALID_KEY) return null;
- synchronized (mListenerMapLock) {
- Object listener = mListenerMap.get(key);
- return listener;
- }
- }
-
- private int getListenerKey(Object listener) {
- if (listener == null) return INVALID_KEY;
- synchronized (mListenerMapLock) {
- int index = mListenerMap.indexOfValue(listener);
- if (index == -1) {
- return INVALID_KEY;
- } else {
- return mListenerMap.keyAt(index);
- }
- }
- }
-
- private Object removeListener(int key) {
- if (key == INVALID_KEY) return null;
- synchronized (mListenerMapLock) {
- Object listener = mListenerMap.get(key);
- mListenerMap.remove(key);
- return listener;
- }
- }
-
- private int removeListener(Object listener) {
- int key = getListenerKey(listener);
- if (key == INVALID_KEY) return key;
- synchronized (mListenerMapLock) {
- mListenerMap.remove(key);
- return key;
- }
- }
-
- private class ServiceHandler extends Handler {
- ServiceHandler(Looper looper) {
- super(looper);
- }
- @Override
- public void handleMessage(Message msg) {
- Log.i(TAG, "RTT manager get message: " + msg.what);
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
- return;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- Log.e(TAG, "Channel connection lost");
- // This will cause all further async API calls on the WifiManager
- // to fail and throw an exception
- mAsyncChannel = null;
- getLooper().quit();
- return;
- }
-
- Object listener = getListener(msg.arg2);
- if (listener == null) {
- Log.e(TAG, "invalid listener key = " + msg.arg2 );
- return;
- } else {
- Log.i(TAG, "listener key = " + msg.arg2);
- }
-
- switch (msg.what) {
- /* ActionListeners grouped together */
- case CMD_OP_SUCCEEDED :
- reportSuccess(listener, msg);
- removeListener(msg.arg2);
- break;
- case CMD_OP_FAILED :
- reportFailure(listener, msg);
- removeListener(msg.arg2);
- break;
- case CMD_OP_ABORTED :
- ((RttListener) listener).onAborted();
- removeListener(msg.arg2);
- break;
- case CMD_OP_ENALBE_RESPONDER_SUCCEEDED:
- ResponderConfig config = (ResponderConfig) msg.obj;
- ((ResponderCallback) (listener)).onResponderEnabled(config);
- break;
- case CMD_OP_ENALBE_RESPONDER_FAILED:
- ((ResponderCallback) (listener)).onResponderEnableFailure(msg.arg1);
- removeListener(msg.arg2);
- break;
- default:
- if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
- return;
- }
- }
-
- void reportSuccess(Object listener, Message msg) {
- RttListener rttListener = (RttListener) listener;
- ParcelableRttResults parcelableResults = (ParcelableRttResults) msg.obj;
- ((RttListener) listener).onSuccess(parcelableResults.mResults);
- }
-
- void reportFailure(Object listener, Message msg) {
- RttListener rttListener = (RttListener) listener;
- Bundle bundle = (Bundle) msg.obj;
- ((RttListener) listener).onFailure(msg.arg1, bundle.getString(DESCRIPTION_KEY));
- }
- }
-
}