Merge "[Magnifier - 1] Initial implementation and wiring"
diff --git a/Android.bp b/Android.bp
index 43fb48a..80f1c37 100644
--- a/Android.bp
+++ b/Android.bp
@@ -65,3 +65,25 @@
optional_subdirs = [
"core/tests/utiltests/jni",
]
+
+java_library {
+ name: "hwbinder",
+ no_framework_libs: true,
+
+ srcs: [
+ "core/java/android/os/HidlSupport.java",
+ "core/java/android/annotation/NonNull.java",
+ "core/java/android/os/HwBinder.java",
+ "core/java/android/os/HwBlob.java",
+ "core/java/android/os/HwParcel.java",
+ "core/java/android/os/IHwBinder.java",
+ "core/java/android/os/IHwInterface.java",
+ "core/java/android/os/DeadObjectException.java",
+ "core/java/android/os/DeadSystemException.java",
+ "core/java/android/os/RemoteException.java",
+ "core/java/android/util/AndroidException.java",
+ ],
+
+ dxflags: ["--core-library"],
+ installable: false,
+}
diff --git a/Android.mk b/Android.mk
index aa7caa5..98e4299 100644
--- a/Android.mk
+++ b/Android.mk
@@ -552,6 +552,8 @@
wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl \
wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl \
wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
+ wifi/java/android/net/wifi/rtt/IRttCallback.aidl \
+ wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl \
wifi/java/android/net/wifi/IWifiScanner.aidl \
wifi/java/android/net/wifi/IRttManager.aidl \
packages/services/PacProcessor/com/android/net/IProxyService.aidl \
@@ -650,32 +652,6 @@
framework_built := $(call java-lib-deps,framework)
-# HwBinder
-# =======================================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- core/java/android/os/HidlSupport.java \
- core/java/android/annotation/NonNull.java \
- core/java/android/os/HwBinder.java \
- core/java/android/os/HwBlob.java \
- core/java/android/os/HwParcel.java \
- core/java/android/os/IHwBinder.java \
- core/java/android/os/IHwInterface.java \
- core/java/android/os/DeadObjectException.java \
- core/java/android/os/DeadSystemException.java \
- core/java/android/os/RemoteException.java \
- core/java/android/util/AndroidException.java \
-
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := hwbinder
-
-LOCAL_DX_FLAGS := --core-library
-LOCAL_UNINSTALLABLE_MODULE := true
-include $(BUILD_JAVA_LIBRARY)
-
# Copy AIDL files to be preprocessed and included in the SDK,
# specified relative to the root of the build tree.
# ============================================================
@@ -721,6 +697,8 @@
frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pGroup.aidl \
frameworks/base/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.aidl \
frameworks/base/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl \
+ frameworks/base/wifi/java/android/net/wifi/rtt/RangingRequest.aidl \
+ frameworks/base/wifi/java/android/net/wifi/rtt/RangingResult.aidl \
frameworks/base/wifi/java/android/net/wifi/WpsInfo.aidl \
frameworks/base/wifi/java/android/net/wifi/ScanResult.aidl \
frameworks/base/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.aidl \
diff --git a/api/current.txt b/api/current.txt
index bc77019..58523f7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6683,6 +6683,7 @@
method public android.graphics.Matrix getTransformation();
method public int getVisibility();
method public java.lang.String getWebDomain();
+ method public java.lang.String getWebScheme();
method public int getWidth();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
@@ -40690,8 +40691,13 @@
public class DownloadStateCallback {
ctor public DownloadStateCallback();
+ ctor public DownloadStateCallback(int);
+ method public final boolean isFilterFlagSet(int);
method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+ field public static final int ALL_UPDATES = 0; // 0x0
+ field public static final int PROGRESS_UPDATES = 1; // 0x1
+ field public static final int STATE_UPDATES = 2; // 0x2
}
public final class FileInfo implements android.os.Parcelable {
@@ -40765,6 +40771,7 @@
public class ServiceInfo {
method public java.util.List<java.util.Locale> getLocales();
method public java.lang.CharSequence getNameForLocale(java.util.Locale);
+ method public java.util.Set<java.util.Locale> getNamedContentLocales();
method public java.lang.String getServiceClassName();
method public java.lang.String getServiceId();
method public java.util.Date getSessionEndTime();
diff --git a/api/system-current.txt b/api/system-current.txt
index 0e082b1..4396dc4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6933,6 +6933,7 @@
method public android.graphics.Matrix getTransformation();
method public int getVisibility();
method public java.lang.String getWebDomain();
+ method public java.lang.String getWebScheme();
method public int getWidth();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
@@ -40890,17 +40891,22 @@
public final class Suggestion implements android.os.Parcelable {
method public int describeContents();
+ method public int getFlags();
+ method public android.graphics.drawable.Icon getIcon();
method public java.lang.String getId();
method public android.app.PendingIntent getPendingIntent();
method public java.lang.CharSequence getSummary();
method public java.lang.CharSequence getTitle();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.settings.suggestions.Suggestion> CREATOR;
+ field public static final int FLAG_HAS_BUTTON = 1; // 0x1
}
public static class Suggestion.Builder {
ctor public Suggestion.Builder(java.lang.String);
method public android.service.settings.suggestions.Suggestion build();
+ method public android.service.settings.suggestions.Suggestion.Builder setFlags(int);
+ method public android.service.settings.suggestions.Suggestion.Builder setIcon(android.graphics.drawable.Icon);
method public android.service.settings.suggestions.Suggestion.Builder setPendingIntent(android.app.PendingIntent);
method public android.service.settings.suggestions.Suggestion.Builder setSummary(java.lang.CharSequence);
method public android.service.settings.suggestions.Suggestion.Builder setTitle(java.lang.CharSequence);
@@ -44287,8 +44293,13 @@
public class DownloadStateCallback {
ctor public DownloadStateCallback();
+ ctor public DownloadStateCallback(int);
+ method public final boolean isFilterFlagSet(int);
method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+ field public static final int ALL_UPDATES = 0; // 0x0
+ field public static final int PROGRESS_UPDATES = 1; // 0x1
+ field public static final int STATE_UPDATES = 2; // 0x2
}
public final class FileInfo implements android.os.Parcelable {
@@ -44371,6 +44382,7 @@
public class ServiceInfo {
method public java.util.List<java.util.Locale> getLocales();
method public java.lang.CharSequence getNameForLocale(java.util.Locale);
+ method public java.util.Set<java.util.Locale> getNamedContentLocales();
method public java.lang.String getServiceClassName();
method public java.lang.String getServiceId();
method public java.util.Date getSessionEndTime();
diff --git a/api/test-current.txt b/api/test-current.txt
index 6192e4b..935219f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6754,6 +6754,7 @@
method public android.graphics.Matrix getTransformation();
method public int getVisibility();
method public java.lang.String getWebDomain();
+ method public java.lang.String getWebScheme();
method public int getWidth();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
@@ -41040,8 +41041,13 @@
public class DownloadStateCallback {
ctor public DownloadStateCallback();
+ ctor public DownloadStateCallback(int);
+ method public final boolean isFilterFlagSet(int);
method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+ field public static final int ALL_UPDATES = 0; // 0x0
+ field public static final int PROGRESS_UPDATES = 1; // 0x1
+ field public static final int STATE_UPDATES = 2; // 0x2
}
public final class FileInfo implements android.os.Parcelable {
@@ -41115,6 +41121,7 @@
public class ServiceInfo {
method public java.util.List<java.util.Locale> getLocales();
method public java.lang.CharSequence getNameForLocale(java.util.Locale);
+ method public java.util.Set<java.util.Locale> getNamedContentLocales();
method public java.lang.String getServiceClassName();
method public java.lang.String getServiceId();
method public java.util.Date getSessionEndTime();
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 7e78d6c..b16188e 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -31,12 +31,13 @@
LOCAL_SRC_FILES += \
iot/iotbootanimation_main.cpp \
- iot/BootAction.cpp
+ iot/BootAction.cpp \
+ iot/BootParameters.cpp \
LOCAL_SHARED_LIBRARIES += \
libandroidthings \
libbase \
- libbinder
+ libbinder \
LOCAL_STATIC_LIBRARIES += cpufeatures
diff --git a/cmds/bootanimation/iot/BootAction.cpp b/cmds/bootanimation/iot/BootAction.cpp
index 665b4d9..fa79744 100644
--- a/cmds/bootanimation/iot/BootAction.cpp
+++ b/cmds/bootanimation/iot/BootAction.cpp
@@ -19,96 +19,20 @@
#define LOG_TAG "BootAction"
#include <dlfcn.h>
-#include <fcntl.h>
-#include <map>
-
-#include <android-base/file.h>
-#include <android-base/strings.h>
-#include <base/json/json_parser.h>
-#include <base/json/json_value_converter.h>
-#include <cpu-features.h>
#include <pio/peripheral_manager_client.h>
#include <utils/Log.h>
-using android::base::ReadFileToString;
-using android::base::RemoveFileIfExists;
-using android::base::Split;
-using android::base::Join;
-using android::base::StartsWith;
-using android::base::EndsWith;
-using base::JSONReader;
-using base::Value;
-
namespace android {
-// Brightness and volume are stored as integer strings in next_boot.json.
-// They are divided by this constant to produce the actual float values in
-// range [0.0, 1.0]. This constant must match its counterpart in
-// DeviceManager.
-constexpr const float kFloatScaleFactor = 1000.0f;
-
-constexpr const char* kNextBootFile = "/data/misc/bootanimation/next_boot.json";
-constexpr const char* kLastBootFile = "/data/misc/bootanimation/last_boot.json";
-
-bool loadParameters(BootAction::SavedBootParameters* parameters)
-{
- std::string contents;
- if (!ReadFileToString(kLastBootFile, &contents)) {
- if (errno != ENOENT)
- ALOGE("Unable to read from %s: %s", kLastBootFile, strerror(errno));
-
- return false;
- }
-
- std::unique_ptr<Value> json = JSONReader::Read(contents);
- if (json.get() == nullptr) return false;
-
- JSONValueConverter<BootAction::SavedBootParameters> converter;
- if (!converter.Convert(*(json.get()), parameters)) return false;
-
- return true;
-}
-
-void BootAction::SavedBootParameters::RegisterJSONConverter(
- JSONValueConverter<SavedBootParameters> *converter) {
- converter->RegisterIntField("brightness", &SavedBootParameters::brightness);
- converter->RegisterIntField("volume", &SavedBootParameters::volume);
- converter->RegisterRepeatedString("param_names",
- &SavedBootParameters::param_names);
- converter->RegisterRepeatedString("param_values",
- &SavedBootParameters::param_values);
-}
-
BootAction::~BootAction() {
if (mLibHandle != nullptr) {
dlclose(mLibHandle);
}
}
-void BootAction::swapBootConfigs() {
- // rename() will fail if next_boot.json doesn't exist, so delete
- // last_boot.json manually first.
- std::string err;
- if (!RemoveFileIfExists(kLastBootFile, &err))
- ALOGE("Unable to delete last boot file: %s", err.c_str());
-
- if (rename(kNextBootFile, kLastBootFile) && errno != ENOENT)
- ALOGE("Unable to swap boot files: %s", strerror(errno));
-
- int fd = open(kNextBootFile, O_CREAT, DEFFILEMODE);
- if (fd == -1) {
- ALOGE("Unable to create next boot file: %s", strerror(errno));
- } else {
- // Make next_boot.json writible to everyone so DeviceManagementService
- // can save parameters there.
- if (fchmod(fd, DEFFILEMODE))
- ALOGE("Unable to set next boot file permissions: %s", strerror(errno));
- close(fd);
- }
-}
-
-bool BootAction::init(const std::string& libraryPath) {
+bool BootAction::init(const std::string& libraryPath,
+ const std::vector<ABootActionParameter>& parameters) {
APeripheralManagerClient* client = nullptr;
ALOGD("Connecting to peripheralmanager");
// Wait for peripheral manager to come up.
@@ -122,27 +46,6 @@
ALOGD("Peripheralmanager is up.");
APeripheralManagerClient_delete(client);
- float brightness = -1.0f;
- float volume = -1.0f;
- std::vector<ABootActionParameter> parameters;
- SavedBootParameters saved_parameters;
-
- if (loadParameters(&saved_parameters)) {
- // TODO(b/65462981): Do something with brightness and volume?
- brightness = saved_parameters.brightness / kFloatScaleFactor;
- volume = saved_parameters.volume / kFloatScaleFactor;
-
- if (saved_parameters.param_names.size() == saved_parameters.param_values.size()) {
- for (size_t i = 0; i < saved_parameters.param_names.size(); i++) {
- parameters.push_back({
- .key = saved_parameters.param_names[i]->c_str(),
- .value = saved_parameters.param_values[i]->c_str()
- });
- }
- } else {
- ALOGW("Parameter names and values size mismatch");
- }
- }
ALOGI("Loading boot action %s", libraryPath.c_str());
mLibHandle = dlopen(libraryPath.c_str(), RTLD_NOW);
diff --git a/cmds/bootanimation/iot/BootAction.h b/cmds/bootanimation/iot/BootAction.h
index 3cd43be..5e2495f 100644
--- a/cmds/bootanimation/iot/BootAction.h
+++ b/cmds/bootanimation/iot/BootAction.h
@@ -17,38 +17,21 @@
#ifndef _BOOTANIMATION_BOOTACTION_H
#define _BOOTANIMATION_BOOTACTION_H
-#include <map>
#include <string>
+#include <vector>
-#include <base/json/json_value_converter.h>
#include <boot_action/boot_action.h> // libandroidthings native API.
#include <utils/RefBase.h>
-using base::JSONValueConverter;
-
namespace android {
class BootAction : public RefBase {
public:
- struct SavedBootParameters {
- int brightness;
- int volume;
- ScopedVector<std::string> param_names;
- ScopedVector<std::string> param_values;
- static void RegisterJSONConverter(
- JSONValueConverter<SavedBootParameters>* converter);
- };
-
~BootAction();
- // Rename next_boot.json to last_boot.json so that we don't repeat
- // parameters if there is a crash before the framework comes up.
- // TODO(b/65462981): Is this what we want to do? Should we swap in the
- // framework instead?
- static void swapBootConfigs();
-
// libraryPath is a fully qualified path to the target .so library.
- bool init(const std::string& libraryPath);
+ bool init(const std::string& libraryPath,
+ const std::vector<ABootActionParameter>& parameters);
// The animation is going to start playing partNumber for the playCount'th
// time, update the action as needed.
diff --git a/cmds/bootanimation/iot/BootParameters.cpp b/cmds/bootanimation/iot/BootParameters.cpp
new file mode 100644
index 0000000..da6ad0d
--- /dev/null
+++ b/cmds/bootanimation/iot/BootParameters.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+#include "BootParameters.h"
+
+#define LOG_TAG "BootParameters"
+
+#include <fcntl.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <base/json/json_parser.h>
+#include <base/json/json_reader.h>
+#include <base/json/json_value_converter.h>
+#include <utils/Log.h>
+
+using android::base::RemoveFileIfExists;
+using android::base::ReadFileToString;
+using base::JSONReader;
+using base::JSONValueConverter;
+using base::Value;
+
+namespace android {
+
+namespace {
+
+// Brightness and volume are stored as integer strings in next_boot.json.
+// They are divided by this constant to produce the actual float values in
+// range [0.0, 1.0]. This constant must match its counterpart in
+// DeviceManager.
+constexpr const float kFloatScaleFactor = 1000.0f;
+
+constexpr const char* kNextBootFile = "/data/misc/bootanimation/next_boot.json";
+constexpr const char* kLastBootFile = "/data/misc/bootanimation/last_boot.json";
+
+void swapBootConfigs() {
+ // rename() will fail if next_boot.json doesn't exist, so delete
+ // last_boot.json manually first.
+ std::string err;
+ if (!RemoveFileIfExists(kLastBootFile, &err))
+ ALOGE("Unable to delete last boot file: %s", err.c_str());
+
+ if (rename(kNextBootFile, kLastBootFile) && errno != ENOENT)
+ ALOGE("Unable to swap boot files: %s", strerror(errno));
+
+ int fd = open(kNextBootFile, O_CREAT, DEFFILEMODE);
+ if (fd == -1) {
+ ALOGE("Unable to create next boot file: %s", strerror(errno));
+ } else {
+ // Make next_boot.json writable to everyone so DeviceManagementService
+ // can save saved_parameters there.
+ if (fchmod(fd, DEFFILEMODE))
+ ALOGE("Unable to set next boot file permissions: %s", strerror(errno));
+ close(fd);
+ }
+}
+
+} // namespace
+
+BootParameters::SavedBootParameters::SavedBootParameters()
+ : brightness(-kFloatScaleFactor), volume(-kFloatScaleFactor) {}
+
+void BootParameters::SavedBootParameters::RegisterJSONConverter(
+ JSONValueConverter<SavedBootParameters>* converter) {
+ converter->RegisterIntField("brightness", &SavedBootParameters::brightness);
+ converter->RegisterIntField("volume", &SavedBootParameters::volume);
+ converter->RegisterRepeatedString("param_names",
+ &SavedBootParameters::param_names);
+ converter->RegisterRepeatedString("param_values",
+ &SavedBootParameters::param_values);
+}
+
+BootParameters::BootParameters() {
+ swapBootConfigs();
+ loadParameters();
+}
+
+void BootParameters::loadParameters() {
+ std::string contents;
+ if (!ReadFileToString(kLastBootFile, &contents)) {
+ if (errno != ENOENT)
+ ALOGE("Unable to read from %s: %s", kLastBootFile, strerror(errno));
+
+ return;
+ }
+
+ std::unique_ptr<Value> json = JSONReader::Read(contents);
+ if (json.get() == nullptr) {
+ return;
+ }
+
+ JSONValueConverter<SavedBootParameters> converter;
+ if (converter.Convert(*(json.get()), &mRawParameters)) {
+ mBrightness = mRawParameters.brightness / kFloatScaleFactor;
+ mVolume = mRawParameters.volume / kFloatScaleFactor;
+
+ if (mRawParameters.param_names.size() == mRawParameters.param_values.size()) {
+ for (size_t i = 0; i < mRawParameters.param_names.size(); i++) {
+ mParameters.push_back({
+ .key = mRawParameters.param_names[i]->c_str(),
+ .value = mRawParameters.param_values[i]->c_str()
+ });
+ }
+ } else {
+ ALOGW("Parameter names and values size mismatch");
+ }
+ }
+}
+
+} // namespace android
diff --git a/cmds/bootanimation/iot/BootParameters.h b/cmds/bootanimation/iot/BootParameters.h
new file mode 100644
index 0000000..ff3b018
--- /dev/null
+++ b/cmds/bootanimation/iot/BootParameters.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef _BOOTANIMATION_BOOT_PARAMETERS_H_
+#define _BOOTANIMATION_BOOT_PARAMETERS_H_
+
+#include <list>
+#include <vector>
+
+#include <base/json/json_value_converter.h>
+#include <boot_action/boot_action.h> // libandroidthings native API.
+
+namespace android {
+
+// Provides access to the parameters set by DeviceManager.reboot().
+class BootParameters {
+public:
+ // Constructor loads the parameters for this boot and swaps the param files
+ // to clear the parameters for next boot.
+ BootParameters();
+
+ // Returns true if volume/brightness were explicitly set on reboot.
+ bool hasVolume() const { return mVolume >= 0; }
+ bool hasBrightness() const { return mBrightness >= 0; }
+
+ // Returns volume/brightness in [0,1], or -1 if unset.
+ float getVolume() const { return mVolume; }
+ float getBrightness() const { return mBrightness; }
+
+ // Returns the additional boot parameters that were set on reboot.
+ const std::vector<ABootActionParameter>& getParameters() const { return mParameters; }
+
+private:
+ // Raw boot saved_parameters loaded from .json.
+ struct SavedBootParameters {
+ int brightness;
+ int volume;
+ ScopedVector<std::string> param_names;
+ ScopedVector<std::string> param_values;
+
+ SavedBootParameters();
+ static void RegisterJSONConverter(
+ ::base::JSONValueConverter<SavedBootParameters>* converter);
+ };
+
+ void loadParameters();
+
+ float mVolume = -1.f;
+ float mBrightness = -1.f;
+ std::vector<ABootActionParameter> mParameters;
+
+ // ABootActionParameter is just a raw pointer so we need to keep the
+ // original strings around to avoid losing them.
+ SavedBootParameters mRawParameters;
+};
+
+} // namespace android
+
+
+#endif // _BOOTANIMATION_BOOT_PARAMETERS_H_
diff --git a/cmds/bootanimation/iot/iotbootanimation_main.cpp b/cmds/bootanimation/iot/iotbootanimation_main.cpp
index 441a140..742f9c24 100644
--- a/cmds/bootanimation/iot/iotbootanimation_main.cpp
+++ b/cmds/bootanimation/iot/iotbootanimation_main.cpp
@@ -28,6 +28,7 @@
#include "BootAction.h"
#include "BootAnimationUtil.h"
+#include "BootParameters.h"
using namespace android;
using android::base::ReadFileToString;
@@ -37,7 +38,11 @@
namespace {
-class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks {public:
+class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks {
+public:
+ BootActionAnimationCallbacks(std::unique_ptr<BootParameters> bootParameters)
+ : mBootParameters(std::move(bootParameters)) {}
+
void init(const Vector<Animation::Part>&) override {
std::string library_path("/oem/lib/");
@@ -51,7 +56,7 @@
library_path += property;
mBootAction = new BootAction();
- if (!mBootAction->init(library_path)) {
+ if (!mBootAction->init(library_path, mBootParameters->getParameters())) {
mBootAction = NULL;
}
};
@@ -86,6 +91,7 @@
};
private:
+ std::unique_ptr<BootParameters> mBootParameters;
sp<BootAction> mBootAction = nullptr;
};
@@ -94,9 +100,8 @@
int main() {
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
- // TODO(b/65462981): Should we set brightness/volume here in case the boot
- // animation is disabled?
- BootAction::swapBootConfigs();
+ // Clear our params for next boot no matter what.
+ std::unique_ptr<BootParameters> bootParameters(new BootParameters());
if (bootAnimationDisabled()) {
ALOGI("boot animation disabled");
@@ -108,7 +113,8 @@
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
- sp<BootAnimation> boot = new BootAnimation(new BootActionAnimationCallbacks());
+ sp<BootAnimation> boot = new BootAnimation(
+ new BootActionAnimationCallbacks(std::move(bootParameters)));
IPCThreadState::self()->joinThreadPool();
return 0;
diff --git a/cmds/statsd/src/stats_events.proto b/cmds/statsd/src/stats_events.proto
index dffc68e..1e17895 100644
--- a/cmds/statsd/src/stats_events.proto
+++ b/cmds/statsd/src/stats_events.proto
@@ -23,13 +23,19 @@
option java_outer_classname = "StatsEventProto";
message StatsEvent {
- oneof log_entry_event {
+ oneof event {
+ // Screen state change.
ScreenStateChange screen_state_change = 2;
+ // Process state change.
ProcessStateChange process_state_change = 1112;
}
}
+// Logs changes in screen state. This event is logged in
+// frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
message ScreenStateChange {
+ // Screen state enums follow the values defined in below file.
+ // frameworks/base/core/java/android/view/Display.java
enum State {
STATE_UNKNOWN = 0;
STATE_OFF = 1;
@@ -38,20 +44,20 @@
STATE_DOZE_SUSPEND = 4;
STATE_VR = 5;
}
+ // New screen state.
optional State display_state = 1;
}
+// Logs changes in process state. This event is logged in
+// frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
message ProcessStateChange {
+ // Type of process event.
enum State {
START = 1;
CRASH = 2;
}
optional State state = 1;
+ // UID associated with the package.
optional int32 uid = 2;
-
- optional string package_name = 1002;
-
- optional int32 package_version = 3;
- optional string package_version_string = 4;
}
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 163a8dc..64b9ae8 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -25,6 +25,7 @@
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
import org.json.JSONException;
import org.json.JSONObject;
@@ -135,12 +136,15 @@
private boolean mLights;
private int mLightColor = DEFAULT_LIGHT_COLOR;
private long[] mVibration;
+ // Bitwise representation of fields that have been changed by the user, preventing the app from
+ // making changes to these fields.
private int mUserLockedFields;
private boolean mVibrationEnabled;
private boolean mShowBadge = DEFAULT_SHOW_BADGE;
private boolean mDeleted = DEFAULT_DELETED;
private String mGroup;
private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
+ // If this is a blockable system notification channel.
private boolean mBlockableSystem = false;
/**
@@ -850,4 +854,35 @@
+ ", mBlockableSystem=" + mBlockableSystem
+ '}';
}
+
+ /** @hide */
+ public void toProto(ProtoOutputStream proto) {
+ proto.write(NotificationChannelProto.ID, mId);
+ proto.write(NotificationChannelProto.NAME, mName);
+ proto.write(NotificationChannelProto.DESCRIPTION, mDesc);
+ proto.write(NotificationChannelProto.IMPORTANCE, mImportance);
+ proto.write(NotificationChannelProto.CAN_BYPASS_DND, mBypassDnd);
+ proto.write(NotificationChannelProto.LOCKSCREEN_VISIBILITY, mLockscreenVisibility);
+ if (mSound != null) {
+ proto.write(NotificationChannelProto.SOUND, mSound.toString());
+ }
+ proto.write(NotificationChannelProto.USE_LIGHTS, mLights);
+ proto.write(NotificationChannelProto.LIGHT_COLOR, mLightColor);
+ if (mVibration != null) {
+ for (long v : mVibration) {
+ proto.write(NotificationChannelProto.VIBRATION, v);
+ }
+ }
+ proto.write(NotificationChannelProto.USER_LOCKED_FIELDS, mUserLockedFields);
+ proto.write(NotificationChannelProto.IS_VIBRATION_ENABLED, mVibrationEnabled);
+ proto.write(NotificationChannelProto.SHOW_BADGE, mShowBadge);
+ proto.write(NotificationChannelProto.IS_DELETED, mDeleted);
+ proto.write(NotificationChannelProto.GROUP, mGroup);
+ if (mAudioAttributes != null) {
+ long aToken = proto.start(NotificationChannelProto.AUDIO_ATTRIBUTES);
+ mAudioAttributes.toProto(proto);
+ proto.end(aToken);
+ }
+ proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem);
+ }
}
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 5173311..5cb7fb7 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -21,6 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
import org.json.JSONException;
import org.json.JSONObject;
@@ -295,4 +296,15 @@
+ ", mChannels=" + mChannels
+ '}';
}
+
+ /** @hide */
+ public void toProto(ProtoOutputStream proto) {
+ proto.write(NotificationChannelGroupProto.ID, mId);
+ proto.write(NotificationChannelGroupProto.NAME, mName.toString());
+ proto.write(NotificationChannelGroupProto.DESCRIPTION, mDescription);
+ proto.write(NotificationChannelGroupProto.IS_BLOCKED, mBlocked);
+ for (NotificationChannel channel : mChannels) {
+ channel.toProto(proto);
+ }
+ }
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index ab70f0e..50f1f36 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -81,10 +81,10 @@
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
-import android.net.nsd.INsdManager;
-import android.net.nsd.NsdManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
+import android.net.nsd.INsdManager;
+import android.net.nsd.NsdManager;
import android.net.wifi.IRttManager;
import android.net.wifi.IWifiManager;
import android.net.wifi.IWifiScanner;
@@ -95,6 +95,8 @@
import android.net.wifi.aware.WifiAwareManager;
import android.net.wifi.p2p.IWifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.rtt.IWifiRttManager;
+import android.net.wifi.rtt.WifiRttManager;
import android.nfc.NfcManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -603,6 +605,16 @@
ConnectivityThread.getInstanceLooper());
}});
+ registerService(Context.WIFI_RTT2_SERVICE, WifiRttManager.class,
+ new CachedServiceFetcher<WifiRttManager>() {
+ @Override
+ public WifiRttManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT2_SERVICE);
+ IWifiRttManager service = IWifiRttManager.Stub.asInterface(b);
+ return new WifiRttManager(ctx.getOuterContext(), service);
+ }});
+
registerService(Context.ETHERNET_SERVICE, EthernetManager.class,
new CachedServiceFetcher<EthernetManager>() {
@Override
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index c208f1d..d9b7cd7 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -674,6 +674,7 @@
ViewNodeText mText;
int mInputType;
+ String mWebScheme;
String mWebDomain;
Bundle mExtras;
LocaleList mLocaleList;
@@ -751,6 +752,7 @@
mInputType = in.readInt();
}
if ((flags&FLAGS_HAS_URL) != 0) {
+ mWebScheme = in.readString();
mWebDomain = in.readString();
}
if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) {
@@ -813,7 +815,7 @@
if (mInputType != 0) {
flags |= FLAGS_HAS_INPUT_TYPE;
}
- if (mWebDomain != null) {
+ if (mWebScheme != null || mWebDomain != null) {
flags |= FLAGS_HAS_URL;
}
if (mLocaleList != null) {
@@ -908,6 +910,7 @@
out.writeInt(mInputType);
}
if ((flags&FLAGS_HAS_URL) != 0) {
+ out.writeString(mWebScheme);
out.writeString(mWebDomain);
}
if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) {
@@ -1265,13 +1268,26 @@
* {@link android.service.autofill.AutofillService} for more details.
*
* @return domain-only part of the document. For example, if the full URL is
- * {@code https://my.site/login?user=my_user}, it returns {@code my.site}.
+ * {@code https://example.com/login?user=my_user}, it returns {@code example.com}.
*/
@Nullable public String getWebDomain() {
return mWebDomain;
}
/**
+ * Returns the scheme of the HTML document represented by this view.
+ *
+ * <p>Typically used when the view associated with the view is a container for an HTML
+ * document.
+ *
+ * @return scheme-only part of the document. For example, if the full URL is
+ * {@code https://example.com/login?user=my_user}, it returns {@code https}.
+ */
+ @Nullable public String getWebScheme() {
+ return mWebScheme;
+ }
+
+ /**
* Returns the HTML properties associated with this view.
*
* <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
@@ -1767,10 +1783,13 @@
@Override
public void setWebDomain(@Nullable String domain) {
if (domain == null) {
+ mNode.mWebScheme = null;
mNode.mWebDomain = null;
return;
}
- mNode.mWebDomain = Uri.parse(domain).getHost();
+ Uri uri = Uri.parse(domain);
+ mNode.mWebScheme = uri.getScheme();
+ mNode.mWebDomain = uri.getHost();
}
@Override
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 87e516c..1434c9b 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -317,7 +317,8 @@
}
/**
- * Whether this job needs the device to be plugged in.
+ * Whether this job requires that the device be charging (or be a non-battery-powered
+ * device connected to permanent power, such as Android TV devices).
*/
public boolean isRequireCharging() {
return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0;
@@ -331,7 +332,10 @@
}
/**
- * Whether this job needs the device to be in an Idle maintenance window.
+ * Whether this job requires that the user <em>not</em> be interacting with the device.
+ *
+ * <p class="note">This is <em>not</em> the same as "doze" or "device idle";
+ * it is purely about the user's direct interactions.</p>
*/
public boolean isRequireDeviceIdle() {
return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0;
@@ -918,9 +922,19 @@
}
/**
- * Specify that to run this job, the device needs to be plugged in. This defaults to
- * false.
- * @param requiresCharging Whether or not the device is plugged in.
+ * Specify that to run this job, the device must be charging (or be a
+ * non-battery-powered device connected to permanent power, such as Android TV
+ * devices). This defaults to {@code false}.
+ *
+ * <p class="note">For purposes of running jobs, a battery-powered device
+ * "charging" is not quite the same as simply being connected to power. If the
+ * device is so busy that the battery is draining despite a power connection, jobs
+ * with this constraint will <em>not</em> run. This can happen during some
+ * common use cases such as video chat, particularly if the device is plugged in
+ * to USB rather than to wall power.
+ *
+ * @param requiresCharging Pass {@code true} to require that the device be
+ * charging in order to run the job.
*/
public Builder setRequiresCharging(boolean requiresCharging) {
mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING)
@@ -942,14 +956,22 @@
}
/**
- * Specify that to run, the job needs the device to be in idle mode. This defaults to
- * false.
- * <p>Idle mode is a loose definition provided by the system, which means that the device
- * is not in use, and has not been in use for some time. As such, it is a good time to
- * perform resource heavy jobs. Bear in mind that battery usage will still be attributed
- * to your application, and surfaced to the user in battery stats.</p>
- * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance
- * window.
+ * When set {@code true}, ensure that this job will not run if the device is in active use.
+ * The default state is {@code false}: that is, the for the job to be runnable even when
+ * someone is interacting with the device.
+ *
+ * <p>This state is a loose definition provided by the system. In general, it means that
+ * the device is not currently being used interactively, and has not been in use for some
+ * time. As such, it is a good time to perform resource heavy jobs. Bear in mind that
+ * battery usage will still be attributed to your application, and surfaced to the user in
+ * battery stats.</p>
+ *
+ * <p class="note">Despite the similar naming, this job constraint is <em>not</em>
+ * related to the system's "device idle" or "doze" states. This constraint only
+ * determines whether a job is allowed to run while the device is directly in use.
+ *
+ * @param requiresDeviceIdle Pass {@code true} to prevent the job from running
+ * while the device is being used interactively.
*/
public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE)
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 693921d..cd9c6a3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3467,6 +3467,19 @@
/**
* Use with {@link #getSystemService} to retrieve a {@link
+ * android.net.wifi.rtt.WifiRttManager} for ranging devices with wifi
+ *
+ * Note: this is a replacement for WIFI_RTT_SERVICE above. It will
+ * be renamed once final implementation in place.
+ *
+ * @see #getSystemService
+ * @see android.net.wifi.rtt.WifiRttManager
+ * @hide
+ */
+ public static final String WIFI_RTT2_SERVICE = "rttmanager2";
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a {@link
* android.net.lowpan.LowpanManager} for handling management of
* LoWPAN access.
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ef8f84b..31ca198 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2330,6 +2330,16 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports Wi-Fi RTT (IEEE 802.11mc).
+ *
+ * @hide RTT_API
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_WIFI_RTT = "android.hardware.wifi.rtt";
+
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports LoWPAN networking.
* @hide
*/
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 3726f47..7f4dee6 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1412,7 +1412,11 @@
*/
public void release(int flags) {
synchronized (mToken) {
- mInternalCount--;
+ if (mInternalCount > 0) {
+ // internal count must only be decreased if it is > 0 or state of
+ // the WakeLock object is broken.
+ mInternalCount--;
+ }
if ((flags & RELEASE_FLAG_TIMEOUT) == 0) {
mExternalCount--;
}
diff --git a/core/java/android/service/settings/suggestions/Suggestion.java b/core/java/android/service/settings/suggestions/Suggestion.java
index f27cc2e..cfeb7fc 100644
--- a/core/java/android/service/settings/suggestions/Suggestion.java
+++ b/core/java/android/service/settings/suggestions/Suggestion.java
@@ -16,12 +16,17 @@
package android.service.settings.suggestions;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.app.PendingIntent;
+import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Data object that has information about a device suggestion.
*
@@ -30,9 +35,27 @@
@SystemApi
public final class Suggestion implements Parcelable {
+ /**
+ * @hide
+ */
+ @IntDef(flag = true, value = {
+ FLAG_HAS_BUTTON,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Flags {
+ }
+
+ /**
+ * Flag for suggestion type with a single button
+ */
+ public static final int FLAG_HAS_BUTTON = 1 << 0;
+
private final String mId;
private final CharSequence mTitle;
private final CharSequence mSummary;
+ private final Icon mIcon;
+ @Flags
+ private final int mFlags;
private final PendingIntent mPendingIntent;
/**
@@ -57,6 +80,22 @@
}
/**
+ * Optional icon for this suggestion.
+ */
+ public Icon getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * Optional flags for this suggestion. This will influence UI when rendering suggestion in
+ * different style.
+ */
+ @Flags
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
* The Intent to launch when the suggestion is activated.
*/
public PendingIntent getPendingIntent() {
@@ -67,6 +106,8 @@
mId = builder.mId;
mTitle = builder.mTitle;
mSummary = builder.mSummary;
+ mIcon = builder.mIcon;
+ mFlags = builder.mFlags;
mPendingIntent = builder.mPendingIntent;
}
@@ -74,6 +115,8 @@
mId = in.readString();
mTitle = in.readCharSequence();
mSummary = in.readCharSequence();
+ mIcon = in.readParcelable(Icon.class.getClassLoader());
+ mFlags = in.readInt();
mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader());
}
@@ -99,6 +142,8 @@
dest.writeString(mId);
dest.writeCharSequence(mTitle);
dest.writeCharSequence(mSummary);
+ dest.writeParcelable(mIcon, flags);
+ dest.writeInt(mFlags);
dest.writeParcelable(mPendingIntent, flags);
}
@@ -109,6 +154,9 @@
private final String mId;
private CharSequence mTitle;
private CharSequence mSummary;
+ private Icon mIcon;
+ @Flags
+ private int mFlags;
private PendingIntent mPendingIntent;
public Builder(String id) {
@@ -135,6 +183,23 @@
}
/**
+ * Sets icon for the suggestion.
+ */
+ public Builder setIcon(Icon icon) {
+ mIcon = icon;
+ return this;
+ }
+
+ /**
+ * Sets a UI type for this suggestion. This will influence UI when rendering suggestion in
+ * different style.
+ */
+ public Builder setFlags(@Flags int flags) {
+ mFlags = flags;
+ return this;
+ }
+
+ /**
* Sets suggestion intent
*/
public Builder setPendingIntent(PendingIntent pendingIntent) {
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index 6e69c8f..b09ccc2 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -156,6 +156,14 @@
}
}
+ /**
+ * Apply the style.
+ *
+ * If StaticLyaout.Builder is not provided in setPara() method, this method measures the styled
+ * text width.
+ * If StaticLayout.Builder is provided in setPara() method, this method just passes the style
+ * information to native code by calling StaticLayout.Builder.addstyleRun() and returns 0.
+ */
float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) {
if (fm != null) {
paint.getFontMetricsInt(fm);
@@ -169,7 +177,8 @@
if (mBuilder == null) {
return paint.getTextRunAdvances(mChars, p, len, p, len, isRtl, mWidths, p);
} else {
- return mBuilder.addStyleRun(paint, p, p + len, isRtl);
+ mBuilder.addStyleRun(paint, p, p + len, isRtl);
+ return 0.0f; // Builder.addStyleRun doesn't return the width.
}
}
@@ -182,7 +191,8 @@
totalAdvance +=
paint.getTextRunAdvances(mChars, q, i - q, q, i - q, isRtl, mWidths, q);
} else {
- totalAdvance += mBuilder.addStyleRun(paint, q, i, isRtl);
+ // Builder.addStyleRun doesn't return the width.
+ mBuilder.addStyleRun(paint, q, i, isRtl);
}
if (i == e) {
break;
@@ -191,7 +201,7 @@
level = mLevels[i];
}
}
- return totalAdvance;
+ return totalAdvance; // If mBuilder is null, the result is zero.
}
float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len,
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index e2c31de..961cd8ee 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -453,10 +453,10 @@
}
}
- /* package */ float addStyleRun(TextPaint paint, int start, int end, boolean isRtl) {
+ /* package */ void addStyleRun(TextPaint paint, int start, int end, boolean isRtl) {
Pair<String, long[]> locHyph = getLocaleAndHyphenatorIfChanged(paint);
- return nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl,
- locHyph.first, locHyph.second);
+ nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl, locHyph.first,
+ locHyph.second);
}
/* package */ void addReplacementRun(TextPaint paint, int start, int end, float width) {
@@ -1541,7 +1541,7 @@
@Nullable int[] indents, @Nullable int[] leftPaddings, @Nullable int[] rightPaddings,
@IntRange(from = 0) int indentsOffset);
- private static native float nAddStyleRun(
+ private static native void nAddStyleRun(
/* non-zero */ long nativePtr, /* non-zero */ long nativePaint,
@IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean isRtl,
@Nullable String languageTags, @Nullable long[] hyphenators);
diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java
index 9afa56d..49f8eea 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -2375,6 +2375,9 @@
if (countString == null) {
countString = "fieldCount=" + fieldCount;
}
+ if (countString.length() > 0) {
+ countString += " ";
+ }
final long fieldType = fieldId & FIELD_TYPE_MASK;
String typeString = getFieldTypeString(fieldType);
@@ -2382,7 +2385,7 @@
typeString = "fieldType=" + fieldType;
}
- return fieldCount + " " + typeString + " tag=" + ((int)fieldId)
+ return countString + typeString + " tag=" + ((int) fieldId)
+ " fieldId=0x" + Long.toHexString(fieldId);
}
diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java
new file mode 100644
index 0000000..449baca
--- /dev/null
+++ b/core/java/android/util/proto/ProtoUtils.java
@@ -0,0 +1,39 @@
+/*
+ * 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.util.proto;
+
+import android.util.AggStats;
+
+/**
+ * This class contains a list of helper functions to write common proto in
+ * //frameworks/base/core/proto/android/base directory
+ */
+public class ProtoUtils {
+
+ /**
+ * Dump AggStats to ProtoOutputStream
+ * @hide
+ */
+ public static void toAggStatsProto(ProtoOutputStream proto, long fieldId,
+ long min, long average, long max) {
+ final long aggStatsToken = proto.start(fieldId);
+ proto.write(AggStats.MIN, min);
+ proto.write(AggStats.AVERAGE, average);
+ proto.write(AggStats.MAX, max);
+ proto.end(aggStatsToken);
+ }
+}
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 0ecd20d..f671c34 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -378,7 +378,7 @@
*
* <p>Typically used when the view is a container for an HTML document.
*
- * @param domain URL representing the domain; only the host part will be used.
+ * @param domain RFC 2396-compliant URI representing the domain.
*/
public abstract void setWebDomain(@Nullable String domain);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 077a901..dfc81b2 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2972,8 +2972,12 @@
return webviewPackage;
}
+ IWebViewUpdateService service = WebViewFactory.getUpdateService();
+ if (service == null) {
+ return null;
+ }
try {
- return WebViewFactory.getUpdateService().getCurrentWebViewPackage();
+ return service.getCurrentWebViewPackage();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 994512f..36b24ff 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -51,9 +51,6 @@
private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
- private static final String NULL_WEBVIEW_FACTORY =
- "com.android.webview.nullwebview.NullWebViewFactoryProvider";
-
public static final String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY =
"persist.sys.webview.vmsize";
@@ -66,6 +63,7 @@
private static WebViewFactoryProvider sProviderInstance;
private static final Object sProviderLock = new Object();
private static PackageInfo sPackageInfo;
+ private static Boolean sWebViewSupported;
// Error codes for loadWebViewNativeLibraryFromPackage
public static final int LIBLOAD_SUCCESS = 0;
@@ -105,6 +103,16 @@
public MissingWebViewPackageException(Exception e) { super(e); }
}
+ private static boolean isWebViewSupported() {
+ // No lock; this is a benign race as Boolean's state is final and the PackageManager call
+ // will always return the same value.
+ if (sWebViewSupported == null) {
+ sWebViewSupported = AppGlobals.getInitialApplication().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_WEBVIEW);
+ }
+ return sWebViewSupported;
+ }
+
/**
* @hide
*/
@@ -135,6 +143,10 @@
*/
public static int loadWebViewNativeLibraryFromPackage(String packageName,
ClassLoader clazzLoader) {
+ if (!isWebViewSupported()) {
+ return LIBLOAD_WRONG_PACKAGE_NAME;
+ }
+
WebViewProviderResponse response = null;
try {
response = getUpdateService().waitForAndGetProvider();
@@ -188,6 +200,11 @@
"For security reasons, WebView is not allowed in privileged processes");
}
+ if (!isWebViewSupported()) {
+ // Device doesn't support WebView; don't try to load it, just throw.
+ throw new UnsupportedOperationException();
+ }
+
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
try {
@@ -410,15 +427,6 @@
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (MissingWebViewPackageException e) {
- // If the package doesn't exist, then try loading the null WebView instead.
- // If that succeeds, then this is a device without WebView support; if it fails then
- // swallow the failure, complain that the real WebView is missing and rethrow the
- // original exception.
- try {
- return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
- } catch (ClassNotFoundException e2) {
- // Ignore.
- }
Log.e(LOGTAG, "Chromium WebView package does not exist", e);
throw new AndroidRuntimeException(e);
}
@@ -483,7 +491,11 @@
/** @hide */
public static IWebViewUpdateService getUpdateService() {
- return IWebViewUpdateService.Stub.asInterface(
- ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
+ if (isWebViewSupported()) {
+ return IWebViewUpdateService.Stub.asInterface(
+ ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
+ } else {
+ return null;
+ }
}
}
diff --git a/core/java/android/webkit/WebViewUpdateService.java b/core/java/android/webkit/WebViewUpdateService.java
index 2f7d685..629891c 100644
--- a/core/java/android/webkit/WebViewUpdateService.java
+++ b/core/java/android/webkit/WebViewUpdateService.java
@@ -31,8 +31,12 @@
* Fetch all packages that could potentially implement WebView.
*/
public static WebViewProviderInfo[] getAllWebViewPackages() {
+ IWebViewUpdateService service = getUpdateService();
+ if (service == null) {
+ return new WebViewProviderInfo[0];
+ }
try {
- return getUpdateService().getAllWebViewPackages();
+ return service.getAllWebViewPackages();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -42,8 +46,12 @@
* Fetch all packages that could potentially implement WebView and are currently valid.
*/
public static WebViewProviderInfo[] getValidWebViewPackages() {
+ IWebViewUpdateService service = getUpdateService();
+ if (service == null) {
+ return new WebViewProviderInfo[0];
+ }
try {
- return getUpdateService().getValidWebViewPackages();
+ return service.getValidWebViewPackages();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -53,8 +61,12 @@
* Used by DevelopmentSetting to get the name of the WebView provider currently in use.
*/
public static String getCurrentWebViewPackageName() {
+ IWebViewUpdateService service = getUpdateService();
+ if (service == null) {
+ return null;
+ }
try {
- return getUpdateService().getCurrentWebViewPackageName();
+ return service.getCurrentWebViewPackageName();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index afa9610..8003575 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -267,7 +267,7 @@
SuggestionRangeSpan mSuggestionRangeSpan;
private Runnable mShowSuggestionRunnable;
- Drawable mCursorDrawable = null;
+ Drawable mDrawableForCursor = null;
private Drawable mSelectHandleLeft;
private Drawable mSelectHandleRight;
@@ -1697,7 +1697,7 @@
mCorrectionHighlighter.draw(canvas, cursorOffsetVertical);
}
- if (highlight != null && selectionStart == selectionEnd && mCursorDrawable != null) {
+ if (highlight != null && selectionStart == selectionEnd && mDrawableForCursor != null) {
drawCursor(canvas, cursorOffsetVertical);
// Rely on the drawable entirely, do not draw the cursor line.
// Has to be done after the IMM related code above which relies on the highlight.
@@ -1892,8 +1892,8 @@
private void drawCursor(Canvas canvas, int cursorOffsetVertical) {
final boolean translate = cursorOffsetVertical != 0;
if (translate) canvas.translate(0, cursorOffsetVertical);
- if (mCursorDrawable != null) {
- mCursorDrawable.draw(canvas);
+ if (mDrawableForCursor != null) {
+ mDrawableForCursor.draw(canvas);
}
if (translate) canvas.translate(0, -cursorOffsetVertical);
}
@@ -1952,7 +1952,7 @@
void updateCursorPosition() {
if (mTextView.mCursorDrawableRes == 0) {
- mCursorDrawable = null;
+ mDrawableForCursor = null;
return;
}
@@ -2333,17 +2333,17 @@
@VisibleForTesting
@Nullable
public Drawable getCursorDrawable() {
- return mCursorDrawable;
+ return mDrawableForCursor;
}
private void updateCursorPosition(int top, int bottom, float horizontal) {
- if (mCursorDrawable == null) {
- mCursorDrawable = mTextView.getContext().getDrawable(
+ if (mDrawableForCursor == null) {
+ mDrawableForCursor = mTextView.getContext().getDrawable(
mTextView.mCursorDrawableRes);
}
- final int left = clampHorizontalPosition(mCursorDrawable, horizontal);
- final int width = mCursorDrawable.getIntrinsicWidth();
- mCursorDrawable.setBounds(left, top - mTempRect.top, left + width,
+ final int left = clampHorizontalPosition(mDrawableForCursor, horizontal);
+ final int width = mDrawableForCursor.getIntrinsicWidth();
+ mDrawableForCursor.setBounds(left, top - mTempRect.top, left + width,
bottom + mTempRect.bottom);
}
@@ -4712,9 +4712,9 @@
@Override
protected int getCursorOffset() {
int offset = super.getCursorOffset();
- if (mCursorDrawable != null) {
- mCursorDrawable.getPadding(mTempRect);
- offset += (mCursorDrawable.getIntrinsicWidth()
+ if (mDrawableForCursor != null) {
+ mDrawableForCursor.getPadding(mTempRect);
+ offset += (mDrawableForCursor.getIntrinsicWidth()
- mTempRect.left - mTempRect.right) / 2;
}
return offset;
@@ -4722,9 +4722,9 @@
@Override
int getCursorHorizontalPosition(Layout layout, int offset) {
- if (mCursorDrawable != null) {
+ if (mDrawableForCursor != null) {
final float horizontal = getHorizontal(layout, offset);
- return clampHorizontalPosition(mCursorDrawable, horizontal) + mTempRect.left;
+ return clampHorizontalPosition(mDrawableForCursor, horizontal) + mTempRect.left;
}
return super.getCursorHorizontalPosition(layout, offset);
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 791a8fa..24ae03c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6281,7 +6281,7 @@
final int horizontalPadding = getCompoundPaddingLeft();
final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true);
- if (mEditor.mCursorDrawable == null) {
+ if (mEditor.mDrawableForCursor == null) {
synchronized (TEMP_RECTF) {
/*
* The reason for this concern about the thickness of the
@@ -6308,7 +6308,7 @@
(int) Math.ceil(verticalPadding + TEMP_RECTF.bottom + thick));
}
} else {
- final Rect bounds = mEditor.mCursorDrawable.getBounds();
+ final Rect bounds = mEditor.mDrawableForCursor.getBounds();
invalidate(bounds.left + horizontalPadding, bounds.top + verticalPadding,
bounds.right + horizontalPadding, bounds.bottom + verticalPadding);
}
@@ -6360,8 +6360,8 @@
int bottom = mLayout.getLineBottom(lineEnd);
// mEditor can be null in case selection is set programmatically.
- if (invalidateCursor && mEditor != null && mEditor.mCursorDrawable != null) {
- final Rect bounds = mEditor.mCursorDrawable.getBounds();
+ if (invalidateCursor && mEditor != null && mEditor.mDrawableForCursor != null) {
+ final Rect bounds = mEditor.mDrawableForCursor.getBounds();
top = Math.min(top, bounds.top);
bottom = Math.max(bottom, bounds.bottom);
}
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index ebedc89..0bc8c483 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -29,6 +29,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import static com.android.internal.app.procstats.ProcessStats.*;
@@ -66,6 +67,8 @@
"cch-activity", "cch-aclient", "cch-empty"
};
+ // State enum is defined in frameworks/base/core/proto/android/service/procstats.proto
+ // Update states must sync enum definition as well, the ordering must not be changed.
static final String[] ADJ_SCREEN_TAGS = new String[] {
"0", "1"
};
@@ -177,6 +180,13 @@
printArrayEntry(pw, STATE_TAGS, state, 1);
}
+ public static void printProcStateTagProto(ProtoOutputStream proto, long screenId, long memId,
+ long stateId, int state) {
+ state = printProto(proto, screenId, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD * STATE_COUNT);
+ state = printProto(proto, memId, ADJ_MEM_TAGS, state, STATE_COUNT);
+ printProto(proto, stateId, STATE_TAGS, state, 1);
+ }
+
public static void printAdjTag(PrintWriter pw, int state) {
state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD);
printArrayEntry(pw, ADJ_MEM_TAGS, state, 1);
@@ -352,6 +362,15 @@
return value - index*mod;
}
+ public static int printProto(ProtoOutputStream proto, long fieldId, String[] array, int value, int mod) {
+ int index = value/mod;
+ if (index >= 0 && index < array.length) {
+ // Valid state enum number starts at 1, 0 stands for unknown.
+ proto.write(fieldId, index + 1);
+ } // else enum default is always zero in proto3
+ return value - index*mod;
+ }
+
public static String collapseString(String pkgName, String itemName) {
if (itemName.startsWith(pkgName)) {
final int ITEMLEN = itemName.length();
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index e0a4053..42c051d 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -21,6 +21,8 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.service.pm.PackageProto;
+import android.service.procstats.ProcessStatsProto;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -29,6 +31,8 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.app.procstats.ProcessStats.PackageState;
@@ -69,6 +73,9 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
public final class ProcessState {
@@ -1157,6 +1164,7 @@
}
}
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("ProcessState{").append(Integer.toHexString(System.identityHashCode(this)))
@@ -1167,4 +1175,77 @@
sb.append("}");
return sb.toString();
}
+
+ public void toProto(ProtoOutputStream proto, String procName, int uid, long now) {
+ proto.write(ProcessStatsProto.PROCESS, procName);
+ proto.write(ProcessStatsProto.UID, uid);
+ if (mNumExcessiveCpu > 0 || mNumCachedKill > 0 ) {
+ final long killToken = proto.start(ProcessStatsProto.KILL);
+ proto.write(ProcessStatsProto.Kill.WAKES, mNumExcessiveWake);
+ proto.write(ProcessStatsProto.Kill.CPU, mNumExcessiveCpu);
+ proto.write(ProcessStatsProto.Kill.CACHED, mNumCachedKill);
+ ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.Kill.CACHED_PSS,
+ mMinCachedKillPss, mAvgCachedKillPss, mMaxCachedKillPss);
+ proto.end(killToken);
+ }
+
+ // Group proc stats by type (screen state + mem state + process state)
+ Map<Integer, Long> durationByState = new HashMap<>();
+ boolean didCurState = false;
+ for (int i=0; i<mDurations.getKeyCount(); i++) {
+ final int key = mDurations.getKeyAt(i);
+ final int type = SparseMappingTable.getIdFromKey(key);
+ long time = mDurations.getValue(key);
+ if (mCurState == type) {
+ didCurState = true;
+ time += now - mStartTime;
+ }
+ durationByState.put(type, time);
+ }
+ if (!didCurState && mCurState != STATE_NOTHING) {
+ durationByState.put(mCurState, now - mStartTime);
+ }
+
+ for (int i=0; i<mPssTable.getKeyCount(); i++) {
+ final int key = mPssTable.getKeyAt(i);
+ final int type = SparseMappingTable.getIdFromKey(key);
+ if (!durationByState.containsKey(type)) {
+ // state without duration should not have stats!
+ continue;
+ }
+ final long stateToken = proto.start(ProcessStatsProto.STATES);
+ DumpUtils.printProcStateTagProto(proto,
+ ProcessStatsProto.State.SCREEN_STATE,
+ ProcessStatsProto.State.MEMORY_STATE,
+ ProcessStatsProto.State.PROCESS_STATE,
+ type);
+
+ long duration = durationByState.get(type);
+ durationByState.remove(type); // remove the key since it is already being dumped.
+ proto.write(ProcessStatsProto.State.DURATION_MS, duration);
+
+ proto.write(ProcessStatsProto.State.SAMPLE_SIZE, mPssTable.getValue(key, PSS_SAMPLE_COUNT));
+ ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.PSS,
+ mPssTable.getValue(key, PSS_MINIMUM),
+ mPssTable.getValue(key, PSS_AVERAGE),
+ mPssTable.getValue(key, PSS_MAXIMUM));
+ ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.USS,
+ mPssTable.getValue(key, PSS_USS_MINIMUM),
+ mPssTable.getValue(key, PSS_USS_AVERAGE),
+ mPssTable.getValue(key, PSS_USS_MAXIMUM));
+
+ proto.end(stateToken);
+ }
+
+ for (Map.Entry<Integer, Long> entry : durationByState.entrySet()) {
+ final long stateToken = proto.start(ProcessStatsProto.STATES);
+ DumpUtils.printProcStateTagProto(proto,
+ ProcessStatsProto.State.SCREEN_STATE,
+ ProcessStatsProto.State.MEMORY_STATE,
+ ProcessStatsProto.State.PROCESS_STATE,
+ entry.getKey());
+ proto.write(ProcessStatsProto.State.DURATION_MS, entry.getValue());
+ proto.end(stateToken);
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 35b53c2..14f5e5b 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -22,6 +22,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.service.procstats.ProcessStatsSectionProto;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -30,6 +31,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.procstats.DurationsTable;
@@ -1706,6 +1708,46 @@
}
}
+ public void toProto(ProtoOutputStream proto, long now) {
+ final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+
+ proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
+ proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
+ mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
+ proto.write(ProcessStatsSectionProto.START_UPTIME_MS, mTimePeriodStartUptime);
+ proto.write(ProcessStatsSectionProto.END_UPTIME_MS, mTimePeriodEndUptime);
+ proto.write(ProcessStatsSectionProto.RUNTIME, mRuntime);
+ proto.write(ProcessStatsSectionProto.HAS_SWAPPED_PSS, mHasSwappedOutPss);
+ boolean partial = true;
+ if ((mFlags&FLAG_SHUTDOWN) != 0) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SHUTDOWN);
+ partial = false;
+ }
+ if ((mFlags&FLAG_SYSPROPS) != 0) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SYSPROPS);
+ partial = false;
+ }
+ if ((mFlags&FLAG_COMPLETE) != 0) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_COMPLETE);
+ partial = false;
+ }
+ if (partial) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_PARTIAL);
+ }
+
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ for (int ip=0; ip<procMap.size(); ip++) {
+ String procName = procMap.keyAt(ip);
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ final int uid = uids.keyAt(iu);
+ final ProcessState procState = uids.valueAt(iu);
+ final long processStateToken = proto.start(ProcessStatsSectionProto.PROCESS_STATS);
+ procState.toProto(proto, procName, uid, now);
+ proto.end(processStateToken);
+ }
+ }
+ }
final public static class ProcessStateHolder {
public final int appVersion;
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index bcc7752..83ffeff 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -234,7 +234,7 @@
}
// Basically similar to Paint.getTextRunAdvances but with C++ interface
-static jfloat nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr, jlong nativePaint, jint start,
+static void nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr, jlong nativePaint, jint start,
jint end, jboolean isRtl, jstring langTags, jlongArray hyphenators) {
minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
Paint* paint = reinterpret_cast<Paint*>(nativePaint);
@@ -245,9 +245,8 @@
typeface);
ScopedNullableUtfString langTagsString(env, langTags);
- float result = b->addStyleRun(&minikinPaint, resolvedTypeface->fFontCollection, style, start,
+ b->addStyleRun(&minikinPaint, resolvedTypeface->fFontCollection, style, start,
end, isRtl, langTagsString.get(), makeHyphenators(env, hyphenators));
- return result;
}
static void nAddReplacementRun(JNIEnv* env, jclass, jlong nativePtr,
@@ -268,7 +267,7 @@
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
{"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
{"nSetupParagraph", "(J[CIFIF[IIIIZ[I[I[II)V", (void*) nSetupParagraph},
- {"nAddStyleRun", "(JJIIZLjava/lang/String;[J)F", (void*) nAddStyleRun},
+ {"nAddStyleRun", "(JJIIZLjava/lang/String;[J)V", (void*) nAddStyleRun},
{"nAddReplacementRun", "(JIIFLjava/lang/String;[J)V", (void*) nAddReplacementRun},
{"nGetWidths", "(J[F)V", (void*) nGetWidths},
{"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[F[F[II)I",
diff --git a/core/proto/android/app/notification_channel.proto b/core/proto/android/app/notification_channel.proto
new file mode 100644
index 0000000..bbc1956
--- /dev/null
+++ b/core/proto/android/app/notification_channel.proto
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "android.app";
+option java_multiple_files = true;
+
+package android.app;
+
+import "frameworks/base/core/proto/android/media/audioattributes.proto";
+
+/**
+ * An android.app.NotificationChannel object.
+ */
+message NotificationChannelProto {
+ string id = 1;
+ string name = 2;
+ string description = 3;
+ int32 importance = 4;
+ bool can_bypass_dnd = 5;
+ // Default is VISIBILITY_NO_OVERRIDE (-1000).
+ int32 lockscreen_visibility = 6;
+ string sound = 7;
+ bool use_lights = 8;
+ // Default is 0.
+ int32 light_color = 9;
+ repeated int64 vibration = 10;
+ // Bitwise representation of fields that have been changed by the user,
+ // preventing the app from making changes to these fields.
+ int32 user_locked_fields = 11;
+ bool is_vibration_enabled = 12;
+ // Default is true.
+ bool show_badge = 13;
+ // Default is false.
+ bool is_deleted = 14;
+ string group = 15;
+ android.media.AudioAttributesProto audio_attributes = 16;
+ // If this is a blockable system notification channel.
+ bool is_blockable_system = 17;
+}
diff --git a/core/proto/android/app/notification_channel_group.proto b/core/proto/android/app/notification_channel_group.proto
new file mode 100644
index 0000000..9cb456f
--- /dev/null
+++ b/core/proto/android/app/notification_channel_group.proto
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "android.app";
+option java_multiple_files = true;
+
+package android.app;
+
+import "frameworks/base/core/proto/android/app/notification_channel.proto";
+
+/**
+ * An android.app.NotificationChannelGroup object.
+ */
+message NotificationChannelGroupProto {
+ string id = 1;
+ string name = 2;
+ string description = 3;
+ bool is_blocked = 4;
+ repeated android.app.NotificationChannelProto channels = 5;
+}
diff --git a/core/proto/android/media/audioattributes.proto b/core/proto/android/media/audioattributes.proto
new file mode 100644
index 0000000..3aa2792
--- /dev/null
+++ b/core/proto/android/media/audioattributes.proto
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "android.media";
+option java_multiple_files = true;
+
+package android.media;
+
+/**
+ * An android.media.AudioAttributes object.
+ */
+message AudioAttributesProto {
+ Usage usage = 1;
+ ContentType content_type = 2;
+ // Bit representation of set flags.
+ int32 flags = 3;
+ repeated string tags = 4;
+}
+
+enum ContentType {
+ // Content type value to use when the content type is unknown, or other than
+ // the ones defined.
+ CONTENT_TYPE_UNKNOWN = 0;
+ // Content type value to use when the content type is speech.
+ SPEECH = 1;
+ // Content type value to use when the content type is music.
+ MUSIC = 2;
+ // Content type value to use when the content type is a soundtrack,
+ // typically accompanying a movie or TV program.
+ MOVIE = 3;
+ // Content type value to use when the content type is a sound used to
+ // accompany a user action, such as a beep or sound effect expressing a key
+ // click, or event, such as the type of a sound for a bonus being received
+ // in a game. These sounds are mostly synthesized or short Foley sounds.
+ SONIFICATION = 4;
+}
+
+enum Usage {
+ // Usage value to use when the usage is unknown.
+ USAGE_UNKNOWN = 0;
+ // Usage value to use when the usage is media, such as music, or movie
+ // soundtracks.
+ MEDIA = 1;
+ // Usage value to use when the usage is voice communications, such as
+ // telephony or VoIP.
+ VOICE_COMMUNICATION = 2;
+ // Usage value to use when the usage is in-call signalling, such as with a
+ // "busy" beep, or DTMF tones.
+ VOICE_COMMUNICATION_SIGNALLING = 3;
+ // Usage value to use when the usage is an alarm (e.g. wake-up alarm).
+ ALARM = 4;
+ // Usage value to use when the usage is notification. Other notification
+ // usages are for more specialized uses.
+ NOTIFICATION = 5;
+ // Usage value to use when the usage is telephony ringtone.
+ NOTIFICATION_RINGTONE = 6;
+ // Usage value to use when the usage is a request to enter/end a
+ // communication, such as a VoIP communication or video-conference.
+ NOTIFICATION_COMMUNICATION_REQUEST = 7;
+ // Usage value to use when the usage is notification for an "instant"
+ // communication such as a chat, or SMS.
+ NOTIFICATION_COMMUNICATION_INSTANT = 8;
+ // Usage value to use when the usage is notification for a non-immediate
+ // type of communication such as e-mail.
+ NOTIFICATION_COMMUNICATION_DELAYED = 9;
+ // Usage value to use when the usage is to attract the user's attention,
+ // such as a reminder or low battery warning.
+ NOTIFICATION_EVENT = 10;
+ // Usage value to use when the usage is for accessibility, such as with a
+ // screen reader.
+ ASSISTANCE_ACCESSIBILITY = 11;
+ // Usage value to use when the usage is driving or navigation directions.
+ ASSISTANCE_NAVIGATION_GUIDANCE = 12;
+ // Usage value to use when the usage is sonification, such as with user
+ // interface sounds.
+ ASSISTANCE_SONIFICATION = 13;
+ // Usage value to use when the usage is for game audio.
+ GAME = 14;
+ // Usage value to use when feeding audio to the platform and replacing
+ // "traditional" audio source, such as audio capture devices.
+ VIRTUAL_SOURCE = 15;
+ // Usage value to use for audio responses to user queries, audio
+ // instructions or help utterances.
+ ASSISTANT = 16;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index e823561..c3ceb1c 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -30,6 +30,7 @@
import "frameworks/base/core/proto/android/service/package.proto";
import "frameworks/base/core/proto/android/service/power.proto";
import "frameworks/base/core/proto/android/service/print.proto";
+import "frameworks/base/core/proto/android/service/procstats.proto";
import "frameworks/base/core/proto/android/providers/settings.proto";
import "frameworks/base/core/proto/android/os/incidentheader.proto";
import "frameworks/base/core/proto/android/os/kernelwake.proto";
@@ -96,4 +97,9 @@
android.service.pm.PackageServiceDumpProto package = 3008;
android.service.power.PowerServiceDumpProto power = 3009;
android.service.print.PrintServiceDumpProto print = 3010;
+
+ android.service.procstats.ProcessStatsServiceDumpProto procstats = 3011 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "procstats --proto"
+ ];
}
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index 05afe52..d8cb1a7 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -21,6 +21,8 @@
option java_multiple_files = true;
option java_outer_classname = "NotificationServiceProto";
+import "frameworks/base/core/proto/android/app/notification_channel.proto";
+import "frameworks/base/core/proto/android/app/notification_channel_group.proto";
import "frameworks/base/core/proto/android/app/notificationmanager.proto";
import "frameworks/base/core/proto/android/content/component_name.proto";
@@ -38,6 +40,8 @@
ManagedServicesProto notification_assistants = 6;
ManagedServicesProto condition_providers = 7;
+
+ RankingHelperProto ranking_config = 8;
}
message NotificationRecordProto {
@@ -86,6 +90,28 @@
repeated android.content.ComponentNameProto snoozed = 5;
}
+message RankingHelperProto {
+ repeated string notification_signal_extractors = 1;
+
+ message RecordProto {
+ string package = 1;
+ // Default value is UNKNOWN_UID = USER_NULL = -10000.
+ int32 uid = 2;
+ // Default is IMPORTANCE_UNSPECIFIED (-1000).
+ int32 importance = 3;
+ // Default is PRIORITY_DEFAULT (0).
+ int32 priority = 4;
+ // Default is VISIBILITY_NO_OVERRIDE (-1000).
+ int32 visibility = 5;
+ // Default is true.
+ bool show_badge = 6;
+ repeated android.app.NotificationChannelProto channels = 7;
+ repeated android.app.NotificationChannelGroupProto channel_groups = 8;
+ }
+ repeated RecordProto records = 2;
+ repeated RecordProto records_restored_without_uid = 3;
+}
+
enum State {
ENQUEUED = 0;
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
new file mode 100644
index 0000000..885150e
--- /dev/null
+++ b/core/proto/android/service/procstats.proto
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_outer_classname = "ProcessStatsServiceProto";
+
+import "frameworks/base/core/proto/android/util/common.proto";
+
+package android.service.procstats;
+
+/**
+ * Data from ProcStatsService Dumpsys
+ *
+ * Next Tag: 4
+ */
+message ProcessStatsServiceDumpProto {
+
+ ProcessStatsSectionProto procstats_now = 1;
+
+ ProcessStatsSectionProto procstats_over_3hrs = 2;
+
+ ProcessStatsSectionProto procstats_over_24hrs = 3;
+}
+
+/**
+ * Data model from /frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
+ * This proto is defined based on the writeToParcel method.
+ *
+ * Next Tag: 9
+ */
+message ProcessStatsSectionProto {
+
+ // Elapsed realtime at start of report.
+ int64 start_realtime_ms = 1;
+
+ // Elapsed realtime at end of report.
+ int64 end_realtime_ms = 2;
+
+ // CPU uptime at start of report.
+ int64 start_uptime_ms = 3;
+
+ // CPU uptime at end of report.
+ int64 end_uptime_ms = 4;
+
+ // System runtime library. e.g. "libdvm.so", "libart.so".
+ string runtime = 5;
+
+ // whether kernel reports swapped pss.
+ bool has_swapped_pss = 6;
+
+ // Data completeness. e.g. "complete", "partial", shutdown", or "sysprops".
+ enum Status {
+ STATUS_UNKNOWN = 0;
+ STATUS_COMPLETE = 1;
+ STATUS_PARTIAL = 2;
+ STATUS_SHUTDOWN = 3;
+ STATUS_SYSPROPS = 4;
+ }
+ repeated Status status = 7;
+
+ // Stats for each process.
+ repeated ProcessStatsProto process_stats = 8;
+}
+
+// Next Tag: 6
+message ProcessStatsProto {
+
+ // Name of process.
+ string process = 1;
+
+ // Uid of the process.
+ int32 uid = 2;
+
+ // Information about how often kills occurred
+ message Kill {
+ // Count of excessive wakes kills
+ int32 wakes = 1;
+
+ // Count of excessive CPU kills
+ int32 cpu = 2;
+
+ // Count of kills when cached
+ int32 cached = 3;
+
+ // PSS stats during cached kill
+ android.util.AggStats cached_pss = 4;
+ }
+ Kill kill = 3;
+
+ message State {
+ enum ScreenState {
+ SCREEN_UNKNOWN = 0;
+ OFF = 1;
+ ON = 2;
+ }
+ ScreenState screen_state = 1;
+
+ enum MemoryState {
+ MEMORY_UNKNOWN = 0;
+ NORMAL = 1; // normal.
+ MODERATE = 2; // moderate memory pressure.
+ LOW = 3; // low memory.
+ CRITICAL = 4; // critical memory.
+ }
+ MemoryState memory_state = 2;
+
+ enum ProcessState {
+ PROCESS_UNKNOWN = 0;
+ // Persistent system process.
+ PERSISTENT = 1;
+ // Top activity; actually any visible activity.
+ TOP = 2;
+ // Important foreground process (ime, wallpaper, etc).
+ IMPORTANT_FOREGROUND = 3;
+ // Important background process.
+ IMPORTANT_BACKGROUND = 4;
+ // Performing backup operation.
+ BACKUP = 5;
+ // Heavy-weight process (currently not used).
+ HEAVY_WEIGHT = 6;
+ // Background process running a service.
+ SERVICE = 7;
+ // Process not running, but would be if there was enough RAM.
+ SERVICE_RESTARTING = 8;
+ // Process running a receiver.
+ RECEIVER = 9;
+ // Process hosting home/launcher app when not on top.
+ HOME = 10;
+ // Process hosting the last app the user was in.
+ LAST_ACTIVITY = 11;
+ // Cached process hosting a previous activity.
+ CACHED_ACTIVITY = 12;
+ // Cached process hosting a client activity.
+ CACHED_ACTIVITY_CLIENT = 13;
+ // Cached process that is empty.
+ CACHED_EMPTY = 14;
+ }
+ ProcessState process_state = 3;
+
+ // Millisecond duration spent in this state
+ int64 duration_ms = 4;
+
+ // # of samples taken
+ int32 sample_size = 5;
+
+ // PSS is memory reserved for this process
+ android.util.AggStats pss = 6;
+
+ // USS is memory shared between processes, divided evenly for accounting
+ android.util.AggStats uss = 7;
+ }
+ repeated State states = 5;
+}
diff --git a/core/proto/android/util/common.proto b/core/proto/android/util/common.proto
new file mode 100644
index 0000000..6dd4c02
--- /dev/null
+++ b/core/proto/android/util/common.proto
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+package android.util;
+
+option java_multiple_files = true;
+
+/**
+ * Very basic data structure used by aggregated stats.
+ */
+message AggStats {
+
+ int64 min = 1;
+
+ int64 average = 2;
+
+ int64 max = 3;
+}
diff --git a/core/res/res/drawable/ic_ab_back_material_settings.xml b/core/res/res/drawable/ic_ab_back_material_settings.xml
new file mode 100644
index 0000000..7325a41
--- /dev/null
+++ b/core/res/res/drawable/ic_ab_back_material_settings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:autoMirrored="true"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@color/white"
+ android:pathData="M20,11H7.62l4.88,-4.88a0.996,0.996 0,1 0,-1.41 -1.41l-6.94,6.94c-0.2,0.2 -0.2,0.51 0,0.71l6.94,6.94a0.996,0.996 0,1 0,1.41 -1.41L7.62,13H20c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1z"/>
+</vector>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 9dafa7a..9bea3ee 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1336,6 +1336,7 @@
<!-- Default theme for Settings and activities launched from Settings. -->
<style name="Theme.Material.Settings" parent="Theme.Material.Light.LightStatusBar">
+ <item name="homeAsUpIndicator">@drawable/ic_ab_back_material_settings</item>
<item name="colorPrimary">@color/primary_material_settings_light</item>
<item name="colorPrimaryDark">@color/primary_dark_material_settings_light</item>
<item name="colorSecondary">@color/secondary_material_settings_light</item>
diff --git a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
index 5548e48..b0ec55d 100644
--- a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
+++ b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
@@ -21,6 +21,8 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -36,6 +38,8 @@
private static final String TEST_ID = "id";
private static final String TEST_TITLE = "title";
private static final String TEST_SUMMARY = "summary";
+
+ private Icon mIcon;
private PendingIntent mTestIntent;
@@ -44,6 +48,7 @@
final Context context = InstrumentationRegistry.getContext();
mTestIntent = PendingIntent.getActivity(context, 0 /* requestCode */,
new Intent(), 0 /* flags */);
+ mIcon = Icon.createWithBitmap(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888));
}
@Test
@@ -51,12 +56,15 @@
final Suggestion suggestion = new Suggestion.Builder(TEST_ID)
.setTitle(TEST_TITLE)
.setSummary(TEST_SUMMARY)
+ .setIcon(mIcon)
.setPendingIntent(mTestIntent)
.build();
assertThat(suggestion.getId()).isEqualTo(TEST_ID);
assertThat(suggestion.getTitle()).isEqualTo(TEST_TITLE);
assertThat(suggestion.getSummary()).isEqualTo(TEST_SUMMARY);
+ assertThat(suggestion.getIcon()).isEqualTo(mIcon);
+ assertThat(suggestion.getFlags()).isEqualTo(0);
assertThat(suggestion.getPendingIntent()).isEqualTo(mTestIntent);
}
@@ -66,6 +74,7 @@
.setTitle(TEST_TITLE)
.setSummary(TEST_SUMMARY)
.setPendingIntent(mTestIntent)
+ .setIcon(mIcon)
.build();
}
@@ -75,6 +84,8 @@
final Suggestion oldSuggestion = new Suggestion.Builder(TEST_ID)
.setTitle(TEST_TITLE)
.setSummary(TEST_SUMMARY)
+ .setIcon(mIcon)
+ .setFlags(Suggestion.FLAG_HAS_BUTTON)
.setPendingIntent(mTestIntent)
.build();
@@ -85,6 +96,9 @@
assertThat(newSuggestion.getId()).isEqualTo(TEST_ID);
assertThat(newSuggestion.getTitle()).isEqualTo(TEST_TITLE);
assertThat(newSuggestion.getSummary()).isEqualTo(TEST_SUMMARY);
+ assertThat(newSuggestion.getIcon().toString()).isEqualTo(mIcon.toString());
+ assertThat(newSuggestion.getFlags())
+ .isEqualTo(Suggestion.FLAG_HAS_BUTTON);
assertThat(newSuggestion.getPendingIntent()).isEqualTo(mTestIntent);
}
}
diff --git a/core/tests/coretests/src/android/text/StaticLayoutLineBreakingTest.java b/core/tests/coretests/src/android/text/StaticLayoutLineBreakingTest.java
index 5f2b9e5..f7dbafa 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutLineBreakingTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutLineBreakingTest.java
@@ -53,7 +53,7 @@
static {
// The test font has following coverage and width.
// U+0020: 10em
- // U+002E (,): 10em
+ // U+002E (.): 10em
// U+0043 (C): 100em
// U+0049 (I): 1em
// U+004C (L): 50em
diff --git a/media/java/android/media/AmrInputStream.java b/media/java/android/media/AmrInputStream.java
index 13c3ac4..efaf224 100644
--- a/media/java/android/media/AmrInputStream.java
+++ b/media/java/android/media/AmrInputStream.java
@@ -30,7 +30,7 @@
*/
public final class AmrInputStream extends InputStream {
private final static String TAG = "AmrInputStream";
-
+
// frame is 20 msec at 8.000 khz
private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000;
@@ -140,19 +140,15 @@
}
}
- // now read encoded data from the encoder (blocking, since we just filled up the
- // encoder's input with data it should be able to output at least one buffer)
- while (true) {
- int index = mCodec.dequeueOutputBuffer(mInfo, -1);
- if (index >= 0) {
- mBufIn = mInfo.size;
- ByteBuffer out = mCodec.getOutputBuffer(index);
- out.get(mBuf, 0 /* offset */, mBufIn /* length */);
- mCodec.releaseOutputBuffer(index, false /* render */);
- if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
- mSawOutputEOS = true;
- }
- break;
+ // now read encoded data from the encoder
+ int index = mCodec.dequeueOutputBuffer(mInfo, 0);
+ if (index >= 0) {
+ mBufIn = mInfo.size;
+ ByteBuffer out = mCodec.getOutputBuffer(index);
+ out.get(mBuf, 0 /* offset */, mBufIn /* length */);
+ mCodec.releaseOutputBuffer(index, false /* render */);
+ if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSawOutputEOS = true;
}
}
}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 3b9a5de..26ead3d 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -19,12 +19,14 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.media.AudioAttributesProto;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -177,7 +179,7 @@
/**
* IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES
- * if applicable.
+ * if applicable, as well as audioattributes.proto.
*/
/**
@@ -850,6 +852,21 @@
}
/** @hide */
+ public void toProto(ProtoOutputStream proto) {
+ proto.write(AudioAttributesProto.USAGE, mUsage);
+ proto.write(AudioAttributesProto.CONTENT_TYPE, mContentType);
+ proto.write(AudioAttributesProto.FLAGS, mFlags);
+ // mFormattedTags is never null due to assignment in Builder or unmarshalling.
+ for (String t : mFormattedTags.split(";")) {
+ t = t.trim();
+ if (t != "") {
+ proto.write(AudioAttributesProto.TAGS, t);
+ }
+ }
+ // TODO: is the data in mBundle useful for debugging?
+ }
+
+ /** @hide */
public String usageToString() {
return usageToString(mUsage);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
index 8fc9fa6..9fbadee 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
@@ -131,7 +131,7 @@
}
public long getTotalBytes() {
- return mStats.getCacheBytes() + mStats.getCodeBytes() + mStats.getDataBytes();
+ return mStats.getAppBytes() + mStats.getDataBytes();
}
}
}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 664dcfc..12455d8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -24,7 +24,6 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
import android.net.NetworkKey;
import android.net.NetworkRequest;
import android.net.NetworkScoreManager;
@@ -36,10 +35,14 @@
import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiNetworkScoreCache.CacheListener;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.provider.Settings;
import android.support.annotation.GuardedBy;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -47,8 +50,12 @@
import android.util.SparseIntArray;
import android.widget.Toast;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,7 +71,7 @@
/**
* Tracks saved or available wifi networks and their state.
*/
-public class WifiTracker {
+public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestroy {
/**
* Default maximum age in millis of cached scored networks in
* {@link AccessPoint#mScoredNetworkCache} to be used for speed label generation.
@@ -80,7 +87,7 @@
* and used so as to assist with in-the-field WiFi connectivity debugging */
public static boolean sVerboseLogging;
- // TODO(b/36733768): Remove flag includeSaved and includePasspoints.
+ // TODO(b/36733768): Remove flag includeSaved
// TODO: Allow control of this?
// Combo scans can take 5-6s to complete - set to 10s.
@@ -96,9 +103,9 @@
private final WifiListener mListener;
private final boolean mIncludeSaved;
private final boolean mIncludeScans;
- private final boolean mIncludePasspoints;
- @VisibleForTesting final MainHandler mMainHandler;
- @VisibleForTesting final WorkHandler mWorkHandler;
+ @VisibleForTesting MainHandler mMainHandler;
+ @VisibleForTesting WorkHandler mWorkHandler;
+ private HandlerThread mWorkThread;
private WifiTrackerNetworkCallback mNetworkCallback;
@@ -142,7 +149,7 @@
private WifiInfo mLastInfo;
private final NetworkScoreManager mNetworkScoreManager;
- private final WifiNetworkScoreCache mScoreCache;
+ private WifiNetworkScoreCache mScoreCache;
private boolean mNetworkScoringUiEnabled;
private long mMaxSpeedLabelScoreCacheAge;
@@ -169,51 +176,43 @@
return filter;
}
+ /**
+ * Use the lifecycle constructor below whenever possible
+ */
+ @Deprecated
public WifiTracker(Context context, WifiListener wifiListener,
boolean includeSaved, boolean includeScans) {
- this(context, wifiListener, null, includeSaved, includeScans);
- }
-
- public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans) {
- this(context, wifiListener, workerLooper, includeSaved, includeScans, false);
- }
-
- public WifiTracker(Context context, WifiListener wifiListener,
- boolean includeSaved, boolean includeScans, boolean includePasspoints) {
- this(context, wifiListener, null, includeSaved, includeScans, includePasspoints);
- }
-
- public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans, boolean includePasspoints) {
- this(context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints,
+ this(context, wifiListener, includeSaved, includeScans,
context.getSystemService(WifiManager.class),
context.getSystemService(ConnectivityManager.class),
context.getSystemService(NetworkScoreManager.class),
- Looper.myLooper(), newIntentFilter());
+ newIntentFilter());
+ }
+
+ public WifiTracker(Context context, WifiListener wifiListener,
+ @NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) {
+ this(context, wifiListener, includeSaved, includeScans,
+ context.getSystemService(WifiManager.class),
+ context.getSystemService(ConnectivityManager.class),
+ context.getSystemService(NetworkScoreManager.class),
+ newIntentFilter());
+ lifecycle.addObserver(this);
}
@VisibleForTesting
- WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans, boolean includePasspoints,
- WifiManager wifiManager, ConnectivityManager connectivityManager,
- NetworkScoreManager networkScoreManager, Looper currentLooper,
- IntentFilter filter) {
+ WifiTracker(Context context, WifiListener wifiListener,
+ boolean includeSaved, boolean includeScans,
+ WifiManager wifiManager, ConnectivityManager connectivityManager,
+ NetworkScoreManager networkScoreManager,
+ IntentFilter filter) {
if (!includeSaved && !includeScans) {
throw new IllegalArgumentException("Must include either saved or scans");
}
mContext = context;
- if (currentLooper == null) {
- // When we aren't on a looper thread, default to the main.
- currentLooper = Looper.getMainLooper();
- }
- mMainHandler = new MainHandler(currentLooper);
- mWorkHandler = new WorkHandler(
- workerLooper != null ? workerLooper : currentLooper);
+ mMainHandler = new MainHandler(Looper.getMainLooper());
mWifiManager = wifiManager;
mIncludeSaved = includeSaved;
mIncludeScans = includeScans;
- mIncludePasspoints = includePasspoints;
mListener = wifiListener;
mConnectivityManager = connectivityManager;
@@ -229,7 +228,22 @@
mNetworkScoreManager = networkScoreManager;
- mScoreCache = new WifiNetworkScoreCache(context, new CacheListener(mWorkHandler) {
+ final HandlerThread workThread = new HandlerThread(TAG
+ + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ workThread.start();
+ setWorkThread(workThread);
+ }
+
+ /**
+ * Sanity warning: this wipes out mScoreCache, so use with extreme caution
+ * @param workThread substitute Handler thread, for testing purposes only
+ */
+ @VisibleForTesting
+ void setWorkThread(HandlerThread workThread) {
+ mWorkThread = workThread;
+ mWorkHandler = new WorkHandler(workThread.getLooper());
+ mScoreCache = new WifiNetworkScoreCache(mContext, new CacheListener(mWorkHandler) {
@Override
public void networkCacheUpdated(List<ScoredNetwork> networks) {
synchronized (mLock) {
@@ -244,6 +258,11 @@
});
}
+ @Override
+ public void onDestroy() {
+ mWorkThread.quit();
+ }
+
/** Synchronously update the list of access points with the latest information. */
@MainThread
public void forceUpdate() {
@@ -312,8 +331,9 @@
* <p>Registers listeners and starts scanning for wifi networks. If this is not called
* then forceUpdate() must be called to populate getAccessPoints().
*/
+ @Override
@MainThread
- public void startTracking() {
+ public void onStart() {
synchronized (mLock) {
registerScoreCache();
@@ -361,15 +381,16 @@
/**
* Stop tracking wifi networks and scores.
*
- * <p>This should always be called when done with a WifiTracker (if startTracking was called) to
+ * <p>This should always be called when done with a WifiTracker (if onStart was called) to
* ensure proper cleanup and prevent any further callbacks from occurring.
*
* <p>Calling this method will set the {@link #mStaleScanResults} bit, which prevents
* {@link WifiListener#onAccessPointsChanged()} callbacks from being invoked (until the bit
* is unset on the next SCAN_RESULTS_AVAILABLE_ACTION).
*/
+ @Override
@MainThread
- public void stopTracking() {
+ public void onStop() {
synchronized (mLock) {
if (mRegistered) {
mContext.unregisterReceiver(mReceiver);
@@ -769,9 +790,8 @@
}
public static List<AccessPoint> getCurrentAccessPoints(Context context, boolean includeSaved,
- boolean includeScans, boolean includePasspoints) {
- WifiTracker tracker = new WifiTracker(context,
- null, null, includeSaved, includeScans, includePasspoints);
+ boolean includeScans) {
+ WifiTracker tracker = new WifiTracker(context, null, includeSaved, includeScans);
tracker.forceUpdate();
tracker.copyAndNotifyListeners(false /*notifyListeners*/);
return tracker.getAccessPoints();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
index 79cee04..8b5863a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
@@ -16,8 +16,10 @@
package com.android.settingslib.wifi;
import android.content.Context;
-import android.os.Looper;
import android.support.annotation.Keep;
+import android.support.annotation.NonNull;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
/**
* Factory method used to inject WifiTracker instances.
@@ -31,12 +33,11 @@
}
public static WifiTracker create(
- Context context, WifiTracker.WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans, boolean includePasspoints) {
+ Context context, WifiTracker.WifiListener wifiListener, @NonNull Lifecycle lifecycle,
+ boolean includeSaved, boolean includeScans) {
if(sTestingWifiTracker != null) {
return sTestingWifiTracker;
}
- return new WifiTracker(
- context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints);
+ return new WifiTracker(context, wifiListener, lifecycle, includeSaved, includeScans);
}
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/StorageStatsSourceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/StorageStatsSourceTest.java
new file mode 100644
index 0000000..3dabe99
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/StorageStatsSourceTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.usage.StorageStats;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class StorageStatsSourceTest {
+ @Test
+ public void AppStorageStatsImpl_totalCorrectly() {
+ StorageStats storageStats = new StorageStats();
+ storageStats.cacheBytes = 1;
+ storageStats.codeBytes = 10;
+ storageStats.dataBytes = 100;
+ StorageStatsSource.AppStorageStatsImpl stats = new StorageStatsSource.AppStorageStatsImpl(
+ storageStats);
+
+ // Note that this does not double add the cache (111).
+ assertThat(stats.getTotalBytes()).isEqualTo(110);
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index f25bb28..4a1d392 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -54,7 +54,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.Looper;
import android.os.SystemClock;
import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
@@ -147,10 +146,7 @@
private CountDownLatch mAccessPointsChangedLatch;
private CountDownLatch mRequestScoresLatch;
private Handler mScannerHandler;
- private HandlerThread mMainThread;
private HandlerThread mWorkerThread;
- private Looper mWorkerLooper;
- private Looper mMainLooper;
private int mOriginalScoringUiSettingValue;
@@ -162,10 +158,6 @@
mWorkerThread = new HandlerThread("TestHandlerWorkerThread");
mWorkerThread.start();
- mWorkerLooper = mWorkerThread.getLooper();
- mMainThread = new HandlerThread("TestHandlerThread");
- mMainThread.start();
- mMainLooper = mMainThread.getLooper();
// Make sure the scanner doesn't try to run on the testing thread.
HandlerThread scannerThread = new HandlerThread("ScannerWorkerThread");
@@ -283,18 +275,17 @@
}
private WifiTracker createMockedWifiTracker() {
- return new WifiTracker(
+ final WifiTracker wifiTracker = new WifiTracker(
mContext,
mockWifiListener,
- mWorkerLooper,
- true,
true,
true,
mockWifiManager,
mockConnectivityManager,
mockNetworkScoreManager,
- mMainLooper,
new IntentFilter()); // empty filter to ignore system broadcasts
+ wifiTracker.setWorkThread(mWorkerThread);
+ return wifiTracker;
}
private void startTracking(WifiTracker tracker) throws InterruptedException {
@@ -302,7 +293,7 @@
mScannerHandler.post(new Runnable() {
@Override
public void run() {
- tracker.startTracking();
+ tracker.onStart();
latch.countDown();
}
});
@@ -406,7 +397,7 @@
scanResult.capabilities = "";
WifiTracker tracker = new WifiTracker(
- InstrumentationRegistry.getTargetContext(), null, mWorkerLooper, true, true);
+ InstrumentationRegistry.getTargetContext(), null, true, true);
AccessPoint result = tracker.getCachedOrCreate(scanResult, new ArrayList<AccessPoint>());
assertTrue(result.mAccessPointListener != null);
@@ -422,7 +413,7 @@
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiTracker tracker = new WifiTracker(
- InstrumentationRegistry.getTargetContext(), null, mWorkerLooper, true, true);
+ InstrumentationRegistry.getTargetContext(), null, true, true);
AccessPoint result = tracker.getCachedOrCreate(configuration, new ArrayList<AccessPoint>());
assertTrue(result.mAccessPointListener != null);
@@ -452,7 +443,7 @@
.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, scoreCache);
// Test unregister
- tracker.stopTracking();
+ tracker.onStop();
assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
verify(mockNetworkScoreManager)
@@ -496,7 +487,7 @@
// Start the tracker and inject the initial scan results and then stop tracking
WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
- tracker.stopTracking();
+ tracker.onStop();
mRequestedKeys.clear();
mRequestScoresLatch = new CountDownLatch(1);
@@ -515,7 +506,7 @@
// Start the tracker and inject the initial scan results and then stop tracking
WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
updateScoresAndWaitForAccessPointsChangedCallback(tracker);
- tracker.stopTracking();
+ tracker.onStop();
assertThat(mScoreCacheCaptor.getValue().getScoredNetwork(NETWORK_KEY_1)).isNotNull();
}
@@ -675,7 +666,7 @@
WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
WifiNetworkScoreCache cache = mScoreCacheCaptor.getValue();
- tracker.stopTracking();
+ tracker.onStop();
verify(mockNetworkScoreManager).unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, cache);
// Verify listener is unregistered so updating a score does not throw an error by posting
@@ -795,7 +786,7 @@
tracker.mMainHandler.sendEmptyMessage(
WifiTracker.MainHandler.MSG_WIFI_STATE_CHANGED);
- tracker.stopTracking();
+ tracker.onStop();
verify(mockWifiListener, atMost(1)).onAccessPointsChanged();
verify(mockWifiListener, atMost(1)).onConnectedChanged();
@@ -821,7 +812,7 @@
startTracking(tracker);
waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
- tracker.stopTracking();
+ tracker.onStop();
waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
startTracking(tracker);
diff --git a/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml b/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml
index db2eb3a..bff97f6 100644
--- a/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml
+++ b/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml
@@ -17,6 +17,6 @@
<corners android:radius="@dimen/borderless_button_radius" />
- <solid android:color="#CC000000" />
+ <solid android:color="?attr/clearAllBackgroundColor" />
</shape>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 16c74e3..c7edb9a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -203,10 +203,6 @@
mStackButtonShadowDistance.x, mStackButtonShadowDistance.y,
mStackButtonShadowColor);
}
- if (Recents.getConfiguration().isLowRamDevice) {
- int bgColor = Utils.getColorAttr(mContext, R.attr.clearAllBackgroundColor);
- mStackActionButton.setBackgroundColor(bgColor);
- }
}
// Let's also require dark status and nav bars if the text is dark
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
index 54e9ed9..492ab44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
@@ -18,8 +18,11 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
@@ -30,6 +33,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Typeface;
+import android.metrics.LogMaker;
import android.os.Bundle;
import android.provider.Settings;
import android.service.notification.SnoozeCriterion;
@@ -63,6 +67,15 @@
private static final int MAX_ASSISTANT_SUGGESTIONS = 1;
private static final String KEY_DEFAULT_SNOOZE = "default";
private static final String KEY_OPTIONS = "options_array";
+ private static final LogMaker OPTIONS_OPEN_LOG =
+ new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS)
+ .setType(MetricsEvent.TYPE_OPEN);
+ private static final LogMaker OPTIONS_CLOSE_LOG =
+ new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS)
+ .setType(MetricsEvent.TYPE_CLOSE);
+ private static final LogMaker UNDO_LOG =
+ new LogMaker(MetricsEvent.NOTIFICATION_UNDO_SNOOZE)
+ .setType(MetricsEvent.TYPE_ACTION);
private NotificationGuts mGutsContainer;
private NotificationSwipeActionHelper mSnoozeListener;
private StatusBarNotification mSbn;
@@ -88,6 +101,8 @@
R.id.action_snooze_longer,
};
+ private MetricsLogger mMetricsLogger = new MetricsLogger();
+
public NotificationSnooze(Context context, AttributeSet attrs) {
super(context, attrs);
mParser = new KeyValueListParser(',');
@@ -123,7 +138,13 @@
mSnoozeOptions = getDefaultSnoozeOptions();
createOptionViews();
- setSelected(mDefaultOption);
+ setSelected(mDefaultOption, false);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ logOptionSelection(MetricsEvent.NOTIFICATION_SNOOZE_CLICKED, mDefaultOption);
}
@Override
@@ -163,7 +184,7 @@
SnoozeOption so = mSnoozeOptions.get(i);
if (so.getAccessibilityAction() != null
&& so.getAccessibilityAction().getId() == action) {
- setSelected(so);
+ setSelected(so, true);
return true;
}
}
@@ -327,12 +348,24 @@
mExpandAnimation.start();
}
- private void setSelected(SnoozeOption option) {
+ private void setSelected(SnoozeOption option, boolean userAction) {
mSelectedOption = option;
mSelectedOptionText.setText(option.getConfirmation());
showSnoozeOptions(false);
hideSelectedOption();
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ if (userAction) {
+ logOptionSelection(MetricsEvent.NOTIFICATION_SELECT_SNOOZE, option);
+ }
+ }
+
+ private void logOptionSelection(int category, SnoozeOption option) {
+ int index = mSnoozeOptions.indexOf(option);
+ long duration = TimeUnit.MINUTES.toMillis(option.getMinutesToSnoozeFor());
+ mMetricsLogger.write(new LogMaker(category)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_INDEX, index)
+ .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS, duration));
}
@Override
@@ -343,13 +376,15 @@
final int id = v.getId();
final SnoozeOption tag = (SnoozeOption) v.getTag();
if (tag != null) {
- setSelected(tag);
+ setSelected(tag, true);
} else if (id == R.id.notification_snooze) {
// Toggle snooze options
showSnoozeOptions(!mExpanded);
+ mMetricsLogger.write(!mExpanded ? OPTIONS_OPEN_LOG : OPTIONS_CLOSE_LOG);
} else {
// Undo snooze was selected
undoSnooze(v);
+ mMetricsLogger.write(UNDO_LOG);
}
}
@@ -380,7 +415,7 @@
@Override
public View getContentView() {
// Reset the view before use
- setSelected(mDefaultOption);
+ setSelected(mDefaultOption, false);
return this;
}
@@ -402,7 +437,7 @@
return true;
} else {
// The view should actually be closed
- setSelected(mSnoozeOptions.get(0));
+ setSelected(mSnoozeOptions.get(0), false);
return false; // Return false here so that guts handles closing the view
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index a2e5a3b..54b4e35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1437,6 +1437,11 @@
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
};
+ if (hideAnimatedList.isEmpty()) {
+ animationFinishAction.run();
+ return;
+ }
+
// let's disable our normal animations
mStackScroller.setDismissAllInProgress(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index c0a6837..0d21c4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager.ActionListener;
-import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -59,13 +58,19 @@
private int mCurrentUser;
- public AccessPointControllerImpl(Context context, Looper bgLooper) {
+ public AccessPointControllerImpl(Context context) {
mContext = context;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mWifiTracker = new WifiTracker(context, this, bgLooper, false, true);
+ mWifiTracker = new WifiTracker(context, this, false, true);
mCurrentUser = ActivityManager.getCurrentUser();
}
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ mWifiTracker.onDestroy();
+ }
+
public boolean canConfigWifi() {
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI,
new UserHandle(mCurrentUser));
@@ -81,7 +86,7 @@
if (DEBUG) Log.d(TAG, "addCallback " + callback);
mCallbacks.add(callback);
if (mCallbacks.size() == 1) {
- mWifiTracker.startTracking();
+ mWifiTracker.onStart();
}
}
@@ -91,7 +96,7 @@
if (DEBUG) Log.d(TAG, "removeCallback " + callback);
mCallbacks.remove(callback);
if (mCallbacks.isEmpty()) {
- mWifiTracker.stopTracking();
+ mWifiTracker.onStop();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 3e9d000..d24e51c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -150,7 +150,7 @@
(WifiManager) context.getSystemService(Context.WIFI_SERVICE),
SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
new CallbackHandler(),
- new AccessPointControllerImpl(context, bgLooper),
+ new AccessPointControllerImpl(context),
new DataUsageController(context),
new SubscriptionDefaults(),
deviceProvisionedController);
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 401705d..e17a6a6 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4538,103 +4538,132 @@
// OS: O MR
AUTOFILL_UI_LATENCY = 1136;
+ // Action: the snooze leave-behind was shown after the user clicked the snooze icon
+ // OS: O MR
+ NOTIFICATION_SNOOZE_CLICKED = 1137;
+
+ // Action: user selected a notification snooze duration from the drop down
+ // OS: O MR
+ NOTIFICATION_SELECT_SNOOZE = 1138;
+
+ // attached to NOTIFICATION_SNOOZED and NOTIFICATION_SELECT_SNOOZE events
+ // OS: O MR
+ FIELD_NOTIFICATION_SNOOZE_DURATION_MS = 1139;
+
+ // attached to NOTIFICATION_SELECT_SNOOZE events to indicate the option selected
+ // OS: O MR
+ FIELD_NOTIFICATION_SNOOZE_INDEX = 1140;
+
+ // Action: user tapped undo on the notification snooze leave-behind
+ // OS: O MR
+ NOTIFICATION_UNDO_SNOOZE = 1141;
+
+ // Action: user togged the visibility of the notification snooze options drop down
+ // OS: O MR
+ NOTIFICATION_SNOOZE_OPTIONS = 1142;
+
// ---- End O-MR1 Constants, all O-MR1 constants go above this line ----
// OPEN: Settings > Network & Internet > Mobile network
// CATEGORY: SETTINGS
- SETTINGS_MOBILE_NETWORK_CATEGORY = 1139;
+ SETTINGS_MOBILE_NETWORK_CATEGORY = 1200;
// ACTION: Settings > Network & Internet > Mobile network > Roaming
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_DATA_ROAMING_TOGGLE = 1140;
+ ACTION_MOBILE_NETWORK_DATA_ROAMING_TOGGLE = 1201;
// ACTION: Settings > Network & Internet > Mobile network > Advanced
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_EXPAND_ADVANCED_FIELDS = 1141;
+ ACTION_MOBILE_NETWORK_EXPAND_ADVANCED_FIELDS = 1202;
// ACTION: Settings > Network & Internet > Mobile network > Enhanced 4G LTE Mode
// CATEGORY: SETTINGS
- ACTION_MOBILE_ENHANCED_4G_LTE_MODE_TOGGLE = 1142;
+ ACTION_MOBILE_ENHANCED_4G_LTE_MODE_TOGGLE = 1203;
// ACTION: Settings > Network & Internet > Mobile network > Preferred network type
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_SELECT_PREFERRED_NETWORK = 1143;
+ ACTION_MOBILE_NETWORK_SELECT_PREFERRED_NETWORK = 1204;
// ACTION: Settings > Network & Internet > Mobile network > Preferred network type (enabled networks)
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_SELECT_ENABLED_NETWORK = 1144;
+ ACTION_MOBILE_NETWORK_SELECT_ENABLED_NETWORK = 1205;
// OPEN: Settings > Network & Internet > Mobile network > Carrier
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_EUICC_SETTING = 1145;
+ ACTION_MOBILE_NETWORK_EUICC_SETTING = 1206;
// OPEN: Settings > Network & Internet > Mobile network > Wi-Fi calling
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_WIFI_CALLING = 1146;
+ ACTION_MOBILE_NETWORK_WIFI_CALLING = 1207;
// ACTION: Settings > Network & Internet > Mobile network > Carrier video calling
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_VIDEO_CALLING_TOGGLE = 1147;
+ ACTION_MOBILE_NETWORK_VIDEO_CALLING_TOGGLE = 1208;
// ACTION: Settings > Network & Internet > Mobile network > Automatically select network
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_AUTO_SELECT_NETWORK_TOGGLE = 1148;
+ ACTION_MOBILE_NETWORK_AUTO_SELECT_NETWORK_TOGGLE = 1209;
// ACTION: Settings > Network & Internet > Mobile network > Network
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK = 1149;
+ ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK = 1210;
// FIELD - Manually selected mobile network
- FIELD_MOBILE_NETWORK = 1150;
+ FIELD_MOBILE_NETWORK = 1211;
// OPEN: Settings > Network & Internet > Mobile network > Access Point Names
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_APN_SETTINGS = 1151;
+ ACTION_MOBILE_NETWORK_APN_SETTINGS = 1212;
// OPEN: Settings > Network & Internet > Mobile network > Carrier settings
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_CARRIER_SETTINGS = 1152;
+ ACTION_MOBILE_NETWORK_CARRIER_SETTINGS = 1213;
// OPEN: Settings > Network & Internet > Mobile network > System select
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_CDMA_SYSTEM_SELECT = 1153;
+ ACTION_MOBILE_NETWORK_CDMA_SYSTEM_SELECT = 1214;
// OPEN: Settings > Network & Internet > Mobile network > CDMA subscription
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_CDMA_SUBSCRIPTION_SELECT = 1154;
+ ACTION_MOBILE_NETWORK_CDMA_SUBSCRIPTION_SELECT = 1215;
// ACTION: Settings > Network & Internet > Mobile network > Set up data service
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_SET_UP_DATA_SERVICE = 1155;
+ ACTION_MOBILE_NETWORK_SET_UP_DATA_SERVICE = 1216;
// OPEN: Settings > Developer Options > Experiment dashboard
// CATEGORY: SETTINGS
- SETTINGS_FEATURE_FLAGS_DASHBOARD = 1156;
+ SETTINGS_FEATURE_FLAGS_DASHBOARD = 1217;
// OPEN: Settings > Notifications > [App] > Topic Notifications
// CATEGORY: SETTINGS
// OS: P
- NOTIFICATION_CHANNEL_GROUP = 1157;
+ NOTIFICATION_CHANNEL_GROUP = 1218;
// OPEN: Settings > Developer options > Enable > Info dialog
// CATEGORY: SETTINGS
// OS: P
- DIALOG_ENABLE_DEVELOPMENT_OPTIONS = 1158;
+ DIALOG_ENABLE_DEVELOPMENT_OPTIONS = 1219;
// OPEN: Settings > Developer options > OEM unlocking > Info dialog
// CATEGORY: SETTINGS
// OS: P
- DIALOG_ENABLE_OEM_UNLOCKING = 1159;
+ DIALOG_ENABLE_OEM_UNLOCKING = 1220;
// OPEN: Settings > Security > Nexus Imprint > [Fingerprint]
// CATEGORY: SETTINGS
// OS: P
- FINGERPRINT_AUTHENTICATE_SIDECAR = 1160;
+ FINGERPRINT_AUTHENTICATE_SIDECAR = 1221;
// OPEN: Settings > Developer options > USB debugging > Info dialog
// CATEGORY: SETTINGS
// OS: P
- DIALOG_ENABLE_ADB = 1161;
+ DIALOG_ENABLE_ADB = 1222;
+
+ // OPEN: Settings > Developer options > Revoke USB debugging authorizations > Info dialog
+ // CATEGORY: SETTINGS
+ // OS: P
+ DIALOG_CLEAR_ADB_KEYS = 1223;
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 39aed7c..effb86c 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -23,11 +23,14 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.service.procstats.ProcessStatsProto;
+import android.service.procstats.ProcessStatsServiceDumpProto;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.DumpUtils;
@@ -622,13 +625,17 @@
long ident = Binder.clearCallingIdentity();
try {
- dumpInner(fd, pw, args);
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ dumpProto(fd);
+ } else {
+ dumpInner(pw, args);
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- private void dumpInner(FileDescriptor fd, PrintWriter pw, String[] args) {
+ private void dumpInner(PrintWriter pw, String[] args) {
final long now = SystemClock.uptimeMillis();
boolean isCheckin = false;
@@ -1038,4 +1045,44 @@
}
}
}
+
+ private void dumpAggregatedStats(ProtoOutputStream proto, int aggregateHours, long now) {
+ ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000
+ - (ProcessStats.COMMIT_PERIOD/2));
+ if (pfd == null) {
+ return;
+ }
+ ProcessStats stats = new ProcessStats(false);
+ InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ stats.read(stream);
+ if (stats.mReadError != null) {
+ return;
+ }
+ stats.toProto(proto, now);
+ }
+
+ private void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ // dump current procstats
+ long nowToken = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW);
+ long now;
+ synchronized (mAm) {
+ now = SystemClock.uptimeMillis();
+ mProcessStats.toProto(proto, now);
+ }
+ proto.end(nowToken);
+
+ // aggregated over last 3 hours procstats
+ long tokenOf3Hrs = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_OVER_3HRS);
+ dumpAggregatedStats(proto, 3, now);
+ proto.end(tokenOf3Hrs);
+
+ // aggregated over last 24 hours procstats
+ long tokenOf24Hrs = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_OVER_24HRS);
+ dumpAggregatedStats(proto, 24, now);
+ proto.end(tokenOf24Hrs);
+
+ proto.flush();
+ }
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 027dc08..ac85e6b 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -517,11 +517,14 @@
} catch (PackageManager.NameNotFoundException e) {
}
}
- if (localForegroundNoti.getSmallIcon() == null) {
+ if (localForegroundNoti.getSmallIcon() == null
+ || nm.getNotificationChannel(localPackageName, appUid,
+ localForegroundNoti.getChannelId()) == null) {
// Notifications whose icon is 0 are defined to not show
// a notification, silently ignoring it. We don't want to
// just ignore it, we want to prevent the service from
// being foreground.
+ // Also every notification needs a channel.
throw new RuntimeException("invalid service notification: "
+ foregroundNoti);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0bc20a2e..c17583f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4158,7 +4158,8 @@
newDevice, AudioSystem.getOutputDeviceName(newDevice)));
}
synchronized (mConnectedDevices) {
- if ((newDevice & DEVICE_MEDIA_UNMUTED_ON_PLUG) != 0
+ if (mNm.getZenMode() != Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
+ && (newDevice & DEVICE_MEDIA_UNMUTED_ON_PLUG) != 0
&& mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted
&& mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0
&& (newDevice & AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0)
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 90dab2c..b4056b3 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -331,7 +331,6 @@
private static final int MSG_UPDATE_INTERFACE_QUOTA = 10;
private static final int MSG_REMOVE_INTERFACE_QUOTA = 11;
private static final int MSG_POLICIES_CHANGED = 13;
- private static final int MSG_SET_FIREWALL_RULES = 14;
private static final int MSG_RESET_FIREWALL_RULES_BY_UID = 15;
private static final int UID_MSG_STATE_CHANGED = 100;
@@ -3138,9 +3137,9 @@
uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW);
}
}
- setUidFirewallRulesAsync(chain, uidRules, CHAIN_TOGGLE_ENABLE);
+ setUidFirewallRulesUL(chain, uidRules, CHAIN_TOGGLE_ENABLE);
} else {
- setUidFirewallRulesAsync(chain, null, CHAIN_TOGGLE_DISABLE);
+ setUidFirewallRulesUL(chain, null, CHAIN_TOGGLE_DISABLE);
}
}
@@ -3207,7 +3206,7 @@
}
}
- setUidFirewallRulesAsync(FIREWALL_CHAIN_STANDBY, uidRules, CHAIN_TOGGLE_NONE);
+ setUidFirewallRulesUL(FIREWALL_CHAIN_STANDBY, uidRules, CHAIN_TOGGLE_NONE);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
@@ -3906,18 +3905,6 @@
removeInterfaceQuota((String) msg.obj);
return true;
}
- case MSG_SET_FIREWALL_RULES: {
- final int chain = msg.arg1;
- final int toggle = msg.arg2;
- final SparseIntArray uidRules = (SparseIntArray) msg.obj;
- if (uidRules != null) {
- setUidFirewallRules(chain, uidRules);
- }
- if (toggle != CHAIN_TOGGLE_NONE) {
- enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE);
- }
- return true;
- }
case MSG_RESET_FIREWALL_RULES_BY_UID: {
resetUidFirewallRules(msg.arg1);
return true;
@@ -4063,15 +4050,20 @@
/**
* Calls {@link #setUidFirewallRules(int, SparseIntArray)} and
- * {@link #enableFirewallChainUL(int, boolean)} asynchronously.
+ * {@link #enableFirewallChainUL(int, boolean)} synchronously.
*
* @param chain firewall chain.
* @param uidRules new UID rules; if {@code null}, only toggles chain state.
* @param toggle whether the chain should be enabled, disabled, or not changed.
*/
- private void setUidFirewallRulesAsync(int chain, @Nullable SparseIntArray uidRules,
+ private void setUidFirewallRulesUL(int chain, @Nullable SparseIntArray uidRules,
@ChainToggleType int toggle) {
- mHandler.obtainMessage(MSG_SET_FIREWALL_RULES, chain, toggle, uidRules).sendToTarget();
+ if (uidRules != null) {
+ setUidFirewallRulesUL(chain, uidRules);
+ }
+ if (toggle != CHAIN_TOGGLE_NONE) {
+ enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE);
+ }
}
/**
@@ -4079,7 +4071,7 @@
* here to netd. It will clean up dead rules and make sure the target chain only contains rules
* specified here.
*/
- private void setUidFirewallRules(int chain, SparseIntArray uidRules) {
+ private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) {
try {
int size = uidRules.size();
int[] uids = new int[size];
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index 4923b06..f1476b3 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -17,8 +17,10 @@
package com.android.server.notification;
import android.app.Notification;
+import android.app.NotificationChannel;
public interface NotificationManagerInternal {
+ NotificationChannel getNotificationChannel(String pkg, int uid, String channelId);
void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
String tag, int id, Notification notification, int userId);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6e84dd2..4cb5d0f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3286,6 +3286,10 @@
long conditionsToken = proto.start(NotificationServiceDumpProto.CONDITION_PROVIDERS);
mConditionProviders.dump(proto, filter);
proto.end(conditionsToken);
+
+ long rankingToken = proto.start(NotificationServiceDumpProto.RANKING_CONFIG);
+ mRankingHelper.dump(proto, filter);
+ proto.end(rankingToken);
}
proto.flush();
@@ -3441,6 +3445,12 @@
*/
private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
@Override
+ public NotificationChannel getNotificationChannel(String pkg, int uid, String
+ channelId) {
+ return mRankingHelper.getNotificationChannel(pkg, uid, channelId, false);
+ }
+
+ @Override
public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
String tag, int id, Notification notification, int userId) {
enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
@@ -3807,6 +3817,8 @@
MetricsLogger.action(r.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_SNOOZED)
.setType(MetricsEvent.TYPE_CLOSE)
+ .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS,
+ mDuration)
.addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
mSnoozeCriterionId == null ? 0 : 1));
boolean wasPosted = removeFromNotificationListsLocked(r);
@@ -5828,10 +5840,9 @@
final DumpFilter filter = new DumpFilter();
for (int ai = 0; ai < args.length; ai++) {
final String a = args[ai];
- if ("--proto".equals(args[0])) {
+ if ("--proto".equals(a)) {
filter.proto = true;
- }
- if ("--noredact".equals(a) || "--reveal".equals(a)) {
+ } else if ("--noredact".equals(a) || "--reveal".equals(a)) {
filter.redact = false;
} else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
if (ai < args.length-1) {
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index fea2464..8783f47 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -36,10 +36,13 @@
import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.RankingHelperProto;
+import android.service.notification.RankingHelperProto.RecordProto;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseBooleanArray;
+import android.util.proto.ProtoOutputStream;
import org.json.JSONArray;
import org.json.JSONException;
@@ -915,8 +918,7 @@
pw.print(" ");
pw.println(mSignalExtractors[i]);
}
- }
- if (filter == null) {
+
pw.print(prefix);
pw.println("per-package config:");
}
@@ -928,6 +930,52 @@
dumpRecords(pw, prefix, filter, mRestoredWithoutUids);
}
+ public void dump(ProtoOutputStream proto, NotificationManagerService.DumpFilter filter) {
+ final int N = mSignalExtractors.length;
+ for (int i = 0; i < N; i++) {
+ proto.write(RankingHelperProto.NOTIFICATION_SIGNAL_EXTRACTORS,
+ mSignalExtractors[i].getClass().getSimpleName());
+ }
+ synchronized (mRecords) {
+ dumpRecords(proto, RankingHelperProto.RECORDS, filter, mRecords);
+ }
+ dumpRecords(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
+ mRestoredWithoutUids);
+ }
+
+ private static void dumpRecords(ProtoOutputStream proto, long fieldId,
+ NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) {
+ final int N = records.size();
+ long fToken;
+ for (int i = 0; i < N; i++) {
+ final Record r = records.valueAt(i);
+ if (filter == null || filter.matches(r.pkg)) {
+ fToken = proto.start(fieldId);
+
+ proto.write(RecordProto.PACKAGE, r.pkg);
+ proto.write(RecordProto.UID, r.uid);
+ proto.write(RecordProto.IMPORTANCE, r.importance);
+ proto.write(RecordProto.PRIORITY, r.priority);
+ proto.write(RecordProto.VISIBILITY, r.visibility);
+ proto.write(RecordProto.SHOW_BADGE, r.showBadge);
+
+ long token;
+ for (NotificationChannel channel : r.channels.values()) {
+ token = proto.start(RecordProto.CHANNELS);
+ channel.toProto(proto);
+ proto.end(token);
+ }
+ for (NotificationChannelGroup group : r.groups.values()) {
+ token = proto.start(RecordProto.CHANNEL_GROUPS);
+ group.toProto(proto);
+ proto.end(token);
+ }
+
+ proto.end(fToken);
+ }
+ }
+ }
+
private static void dumpRecords(PrintWriter pw, String prefix,
NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) {
final int N = records.size();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d7ec7b6..92cbd3d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -668,9 +668,11 @@
traceEnd();
// Tracks whether the updatable WebView is in a ready state and watches for update installs.
- traceBeginAndSlog("StartWebViewUpdateService");
- mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
- traceEnd();
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+ traceBeginAndSlog("StartWebViewUpdateService");
+ mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
+ traceEnd();
+ }
}
/**
@@ -1091,6 +1093,14 @@
traceBeginAndSlog("StartWifiRtt");
mSystemServiceManager.startService("com.android.server.wifi.RttService");
traceEnd();
+
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_RTT)) {
+ traceBeginAndSlog("StartRttService");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.rtt.RttService");
+ traceEnd();
+ }
}
if (context.getPackageManager().hasSystemFeature(
@@ -1098,8 +1108,6 @@
traceBeginAndSlog("StartWifiAware");
mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
traceEnd();
- } else {
- Slog.i(TAG, "No Wi-Fi Aware Service (Aware support Not Present)");
}
if (context.getPackageManager().hasSystemFeature(
@@ -1687,10 +1695,10 @@
traceEnd();
// No dependency on Webview preparation in system server. But this should
- // be completed before allowring 3rd party
+ // be completed before allowing 3rd party
final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation";
Future<?> webviewPrep = null;
- if (!mOnlyCore) {
+ if (!mOnlyCore && mWebViewUpdateService != null) {
webviewPrep = SystemServerInitThreadPool.get().submit(() -> {
Slog.i(TAG, WEBVIEW_PREPARATION);
TimingsTraceLog traceLog = new TimingsTraceLog(
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index ebac041..764b7b2 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -522,8 +522,7 @@
* @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on.
*/
public void registerStateCallback(@NonNull DownloadRequest request,
- @NonNull DownloadStateCallback callback,
- @NonNull Handler handler) {
+ @NonNull DownloadStateCallback callback, @NonNull Handler handler) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound");
@@ -533,7 +532,8 @@
new InternalDownloadStateCallback(callback, handler);
try {
- int result = downloadService.registerStateCallback(request, internalCallback);
+ int result = downloadService.registerStateCallback(request, internalCallback,
+ callback.getCallbackFilterFlags());
if (result != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
diff --git a/telephony/java/android/telephony/mbms/DownloadStateCallback.java b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
index 86920bd..892fbf0 100644
--- a/telephony/java/android/telephony/mbms/DownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
@@ -16,8 +16,12 @@
package android.telephony.mbms;
+import android.annotation.IntDef;
import android.telephony.MbmsDownloadSession;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A optional listener class used by download clients to track progress. Apps should extend this
* class and pass an instance into
@@ -29,6 +33,71 @@
public class DownloadStateCallback {
/**
+ * Bitmask flags used for filtering out callback methods. Used when constructing the
+ * DownloadStateCallback as an optional parameter.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES})
+ public @interface FilterFlag {}
+
+ /**
+ * Receive all callbacks.
+ * Default value.
+ */
+ public static final int ALL_UPDATES = 0x00;
+ /**
+ * Receive callbacks for {@link #onProgressUpdated}.
+ */
+ public static final int PROGRESS_UPDATES = 0x01;
+ /**
+ * Receive callbacks for {@link #onStateUpdated}.
+ */
+ public static final int STATE_UPDATES = 0x02;
+
+ private final int mCallbackFilterFlags;
+
+ /**
+ * Creates a DownloadStateCallback that will receive all callbacks.
+ */
+ public DownloadStateCallback() {
+ mCallbackFilterFlags = ALL_UPDATES;
+ }
+
+ /**
+ * Creates a DownloadStateCallback that will only receive callbacks for the methods specified
+ * via the filterFlags parameter.
+ * @param filterFlags A bitmask of filter flags that will specify which callback this instance
+ * is interested in.
+ */
+ public DownloadStateCallback(int filterFlags) {
+ mCallbackFilterFlags = filterFlags;
+ }
+
+ /**
+ * Return the currently set filter flags.
+ * @return An integer containing the bitmask of flags that this instance is interested in.
+ * @hide
+ */
+ public int getCallbackFilterFlags() {
+ return mCallbackFilterFlags;
+ }
+
+ /**
+ * Returns true if a filter flag is set for a particular callback method. If the flag is set,
+ * the callback will be delivered to the listening process.
+ * @param flag A filter flag specifying whether or not a callback method is registered to
+ * receive callbacks.
+ * @return true if registered to receive callbacks in the listening process, false if not.
+ */
+ public final boolean isFilterFlagSet(@FilterFlag int flag) {
+ if (mCallbackFilterFlags == ALL_UPDATES) {
+ return true;
+ }
+ return (mCallbackFilterFlags & flag) > 0;
+ }
+
+ /**
* Called when the middleware wants to report progress for a file in a {@link DownloadRequest}.
*
* @param request a {@link DownloadRequest}, indicating which download is being referenced.
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 61415b5..fe27537 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -165,6 +165,12 @@
Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
return false;
}
+ // We do not need to verify below extras if the result is not success.
+ if (MbmsDownloadSession.RESULT_SUCCESSFUL !=
+ intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
+ MbmsDownloadSession.RESULT_CANCELLED)) {
+ return true;
+ }
if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST)) {
Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
return false;
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java
index 9a01ed0..8529f52 100644
--- a/telephony/java/android/telephony/mbms/ServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/ServiceInfo.java
@@ -23,6 +23,7 @@
import android.text.TextUtils;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -62,12 +63,6 @@
throw new RuntimeException("bad locales length " + newLocales.size());
}
- for (Locale l : newLocales) {
- if (!newNames.containsKey(l)) {
- throw new IllegalArgumentException("A name must be provided for each locale");
- }
- }
-
names = new HashMap(newNames.size());
names.putAll(newNames);
className = newClassName;
@@ -127,7 +122,7 @@
* Get the user-displayable name for this cell-broadcast service corresponding to the
* provided {@link Locale}.
* @param locale The {@link Locale} in which you want the name of the service. This must be a
- * value from the list returned by {@link #getLocales()} -- an
+ * value from the set returned by {@link #getNamedContentLocales()} -- an
* {@link java.util.NoSuchElementException} may be thrown otherwise.
* @return The {@link CharSequence} providing the name of the service in the given
* {@link Locale}
@@ -140,6 +135,17 @@
}
/**
+ * Return an unmodifiable set of the current {@link Locale}s that have a user-displayable name
+ * associated with them. The user-displayable name associated with any {@link Locale} in this
+ * set can be retrieved with {@link #getNameForLocale(Locale)}.
+ * @return An unmodifiable set of {@link Locale} objects corresponding to a user-displayable
+ * content name in that locale.
+ */
+ public @NonNull Set<Locale> getNamedContentLocales() {
+ return Collections.unmodifiableSet(names.keySet());
+ }
+
+ /**
* The class name for this service - used to categorize and filter
*/
public String getServiceClassName() {
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index ed5e826..cb93542 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -36,7 +36,8 @@
int download(in DownloadRequest downloadRequest);
- int registerStateCallback(in DownloadRequest downloadRequest, IDownloadStateCallback listener);
+ int registerStateCallback(in DownloadRequest downloadRequest, IDownloadStateCallback listener,
+ int flags);
int unregisterStateCallback(in DownloadRequest downloadRequest,
IDownloadStateCallback listener);
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index d845a57..2f85a1d 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -46,6 +46,47 @@
private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>();
private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>();
+
+ // Filters the DownloadStateCallbacks by its configuration from the app.
+ private abstract static class FilteredDownloadStateCallback extends DownloadStateCallback {
+
+ private final IDownloadStateCallback mCallback;
+ public FilteredDownloadStateCallback(IDownloadStateCallback callback, int callbackFlags) {
+ super(callbackFlags);
+ mCallback = callback;
+ }
+
+ @Override
+ public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
+ int currentDownloadSize, int fullDownloadSize, int currentDecodedSize,
+ int fullDecodedSize) {
+ if (!isFilterFlagSet(PROGRESS_UPDATES)) {
+ return;
+ }
+ try {
+ mCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+ fullDownloadSize, currentDecodedSize, fullDecodedSize);
+ } catch (RemoteException e) {
+ onRemoteException(e);
+ }
+ }
+
+ @Override
+ public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+ @MbmsDownloadSession.DownloadStatus int state) {
+ if (!isFilterFlagSet(STATE_UPDATES)) {
+ return;
+ }
+ try {
+ mCallback.onStateUpdated(request, fileInfo, state);
+ } catch (RemoteException e) {
+ onRemoteException(e);
+ }
+ }
+
+ protected abstract void onRemoteException(RemoteException e);
+ }
+
/**
* Initialize the download service for this app and subId, registering the listener.
*
@@ -196,9 +237,8 @@
* @hide
*/
@Override
- public final int registerStateCallback(
- final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
- throws RemoteException {
+ public final int registerStateCallback(final DownloadRequest downloadRequest,
+ final IDownloadStateCallback callback, int flags) throws RemoteException {
final int uid = Binder.getCallingUid();
DeathRecipient deathRecipient = new DeathRecipient() {
@Override
@@ -211,28 +251,10 @@
mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
callback.asBinder().linkToDeath(deathRecipient, 0);
- DownloadStateCallback exposedCallback = new DownloadStateCallback() {
+ DownloadStateCallback exposedCallback = new FilteredDownloadStateCallback(callback, flags) {
@Override
- public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo, int
- currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int
- fullDecodedSize) {
- try {
- callback.onProgressUpdated(request, fileInfo, currentDownloadSize,
- fullDownloadSize,
- currentDecodedSize, fullDecodedSize);
- } catch (RemoteException e) {
- onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
- }
- }
-
- @Override
- public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
- @MbmsDownloadSession.DownloadStatus int state) {
- try {
- callback.onStateUpdated(request, fileInfo, state);
- } catch (RemoteException e) {
- onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
- }
+ protected void onRemoteException(RemoteException e) {
+ onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
}
};
diff --git a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
index 8fb27b2..a43f122 100644
--- a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
+++ b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
@@ -38,8 +38,9 @@
/**
* The MBMS middleware should send this when a download of single file has completed or
- * failed. Mandatory extras are
+ * failed. The only mandatory extra is
* {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_RESULT}
+ * and the following are required when the download has completed:
* {@link MbmsDownloadSession#EXTRA_MBMS_FILE_INFO}
* {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_REQUEST}
* {@link #EXTRA_TEMP_LIST}
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index b80780e..6f2b865 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -20,11 +20,15 @@
#include "ValueVisitor.h"
#include "flatten/Archive.h"
#include "flatten/TableFlattener.h"
+#include "flatten/XmlFlattener.h"
#include "io/BigBufferInputStream.h"
#include "io/Util.h"
+#include "xml/XmlDom.h"
namespace aapt {
+using xml::XmlResource;
+
std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context,
const android::StringPiece& path) {
Source source(path);
@@ -52,6 +56,7 @@
if (!parser.Parse()) {
return {};
}
+
return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
}
@@ -63,7 +68,7 @@
bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table,
const TableFlattenerOptions& options, FilterChain* filters,
- IArchiveWriter* writer) {
+ IArchiveWriter* writer, XmlResource* manifest) {
std::set<std::string> referenced_resources;
// List the files being referenced in the resource table.
for (auto& pkg : split_table->packages) {
@@ -119,6 +124,20 @@
return false;
}
+ } else if (manifest != nullptr && path == "AndroidManifest.xml") {
+ BigBuffer buffer(8192);
+ XmlFlattener xml_flattener(&buffer, {});
+ if (!xml_flattener.Consume(context, manifest)) {
+ context->GetDiagnostics()->Error(DiagMessage(path) << "flattening failed");
+ return false;
+ }
+
+ uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
+ io::BigBufferInputStream manifest_buffer_in(&buffer);
+ if (!io::CopyInputStreamToArchive(context, &manifest_buffer_in, path, compression_flags,
+ writer)) {
+ return false;
+ }
} else {
uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
if (!io::CopyFileToArchive(context, file, path, compression_flags, writer)) {
@@ -129,4 +148,26 @@
return true;
}
+std::unique_ptr<xml::XmlResource> LoadedApk::InflateManifest(IAaptContext* context) {
+ IDiagnostics* diag = context->GetDiagnostics();
+
+ io::IFile* manifest_file = GetFileCollection()->FindFile("AndroidManifest.xml");
+ if (manifest_file == nullptr) {
+ diag->Error(DiagMessage(source_) << "no AndroidManifest.xml found");
+ return {};
+ }
+
+ std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
+ if (manifest_data == nullptr) {
+ diag->Error(DiagMessage(manifest_file->GetSource()) << "could not open AndroidManifest.xml");
+ return {};
+ }
+
+ std::unique_ptr<xml::XmlResource> manifest =
+ xml::Inflate(manifest_data->data(), manifest_data->size(), diag, manifest_file->GetSource());
+ if (manifest == nullptr) {
+ diag->Error(DiagMessage() << "failed to read binary AndroidManifest.xml");
+ }
+ return manifest;
+}
} // namespace aapt
diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h
index dacd0c2..d779b7e 100644
--- a/tools/aapt2/LoadedApk.h
+++ b/tools/aapt2/LoadedApk.h
@@ -25,17 +25,17 @@
#include "flatten/TableFlattener.h"
#include "io/ZipArchive.h"
#include "unflatten/BinaryResourceParser.h"
+#include "xml/XmlDom.h"
namespace aapt {
/** Info about an APK loaded in memory. */
class LoadedApk {
public:
- LoadedApk(
- const Source& source,
- std::unique_ptr<io::IFileCollection> apk,
- std::unique_ptr<ResourceTable> table)
- : source_(source), apk_(std::move(apk)), table_(std::move(table)) {}
+ LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk,
+ std::unique_ptr<ResourceTable> table)
+ : source_(source), apk_(std::move(apk)), table_(std::move(table)) {
+ }
io::IFileCollection* GetFileCollection() { return apk_.get(); }
@@ -51,13 +51,20 @@
IArchiveWriter* writer);
/**
- * Writes the APK on disk at the given path, while also removing the resource
- * files that are not referenced in the resource table. The provided filter
- * chain is applied to each entry in the APK file.
+ * Writes the APK on disk at the given path, while also removing the resource files that are not
+ * referenced in the resource table. The provided filter chain is applied to each entry in the APK
+ * file.
+ *
+ * If the manifest is also provided, it will be written to the new APK file, otherwise the
+ * original manifest will be written. The manifest is only required if the contents of the new APK
+ * have been modified in a way that require the AndroidManifest.xml to also be modified.
*/
virtual bool WriteToArchive(IAaptContext* context, ResourceTable* split_table,
const TableFlattenerOptions& options, FilterChain* filters,
- IArchiveWriter* writer);
+ IArchiveWriter* writer, xml::XmlResource* manifest = nullptr);
+
+ /** Inflates the AndroidManifest.xml file from the APK. */
+ std::unique_ptr<xml::XmlResource> InflateManifest(IAaptContext* context);
static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context,
const android::StringPiece& path);
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 704faee..56b61d0 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -283,24 +283,8 @@
bool ExtractAppDataFromManifest(OptimizeContext* context, LoadedApk* apk,
OptimizeOptions* out_options) {
- io::IFile* manifest_file = apk->GetFileCollection()->FindFile("AndroidManifest.xml");
- if (manifest_file == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "missing AndroidManifest.xml");
- return false;
- }
-
- std::unique_ptr<io::IData> data = manifest_file->OpenAsData();
- if (data == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage(manifest_file->GetSource())
- << "failed to open file");
- return false;
- }
-
- std::unique_ptr<xml::XmlResource> manifest = xml::Inflate(
- data->data(), data->size(), context->GetDiagnostics(), manifest_file->GetSource());
+ std::unique_ptr<xml::XmlResource> manifest = apk->InflateManifest(context);
if (manifest == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage() << "failed to read binary AndroidManifest.xml");
return false;
}
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index 9d6d328..a79a577 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -27,6 +27,7 @@
#include "ConfigDescription.h"
#include "Diagnostics.h"
+#include "ResourceUtils.h"
#include "io/File.h"
#include "io/FileSystem.h"
#include "io/StringInputStream.h"
@@ -329,15 +330,32 @@
// TODO: Validate all references in the configuration are valid. It should be safe to assume from
// this point on that any references from one section to another will be present.
+ // TODO: Automatically arrange artifacts so that they match Play Store multi-APK requirements.
+ // see: https://developer.android.com/google/play/publishing/multiple-apks.html
+ //
+ // For now, make sure the version codes are unique.
+ std::vector<Artifact>& artifacts = config.artifacts;
+ std::sort(artifacts.begin(), artifacts.end());
+ if (std::adjacent_find(artifacts.begin(), artifacts.end()) != artifacts.end()) {
+ diag_->Error(DiagMessage() << "Configuration has duplicate versions");
+ return {};
+ }
+
return {config};
}
ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ =
[](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+ // This will be incremented later so the first version will always be different to the base APK.
+ int current_version = (config->artifacts.empty()) ? 0 : config->artifacts.back().version;
+
Artifact artifact{};
+ Maybe<int> version;
for (const auto& attr : root_element->attributes) {
if (attr.name == "name") {
artifact.name = attr.value;
+ } else if (attr.name == "version") {
+ version = std::stoi(attr.value);
} else if (attr.name == "abi-group") {
artifact.abi_group = {attr.value};
} else if (attr.name == "screen-density-group") {
@@ -355,6 +373,9 @@
<< attr.value);
}
}
+
+ artifact.version = (version) ? version.value() : current_version + 1;
+
config->artifacts.push_back(artifact);
return true;
};
@@ -499,11 +520,11 @@
AndroidSdk entry;
for (const auto& attr : child->attributes) {
if (attr.name == "minSdkVersion") {
- entry.min_sdk_version = {attr.value};
+ entry.min_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
} else if (attr.name == "targetSdkVersion") {
- entry.target_sdk_version = {attr.value};
+ entry.target_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
} else if (attr.name == "maxSdkVersion") {
- entry.max_sdk_version = {attr.value};
+ entry.max_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
} else {
diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value);
}
diff --git a/tools/aapt2/configuration/ConfigurationParser.h b/tools/aapt2/configuration/ConfigurationParser.h
index 9bc9081..c5d3284 100644
--- a/tools/aapt2/configuration/ConfigurationParser.h
+++ b/tools/aapt2/configuration/ConfigurationParser.h
@@ -17,6 +17,7 @@
#ifndef AAPT2_CONFIGURATION_H
#define AAPT2_CONFIGURATION_H
+#include <set>
#include <string>
#include <unordered_map>
#include <vector>
@@ -41,6 +42,12 @@
struct Artifact {
/** Name to use for output of processing foo.apk -> foo.<name>.apk. */
Maybe<std::string> name;
+ /**
+ * Value to add to the base Android manifest versionCode. If it is not present in the
+ * configuration file, it is set to the previous artifact + 1. If the first artifact does not have
+ * a value, artifacts are a 1 based index.
+ */
+ int version;
/** If present, uses the ABI group with this name. */
Maybe<std::string> abi_group;
/** If present, uses the screen density group with this name. */
@@ -60,6 +67,15 @@
/** Convert an artifact name template into a name string based on configuration contents. */
Maybe<std::string> Name(const android::StringPiece& apk_name, IDiagnostics* diag) const;
+
+ bool operator<(const Artifact& rhs) const {
+ // TODO(safarmer): Order by play store multi-APK requirements.
+ return version < rhs.version;
+ }
+
+ bool operator==(const Artifact& rhs) const {
+ return version == rhs.version;
+ }
};
/** Enumeration of currently supported ABIs. */
@@ -103,14 +119,14 @@
};
struct AndroidSdk {
- Maybe<std::string> min_sdk_version;
- Maybe<std::string> target_sdk_version;
- Maybe<std::string> max_sdk_version;
+ Maybe<int> min_sdk_version;
+ Maybe<int> target_sdk_version;
+ Maybe<int> max_sdk_version;
Maybe<AndroidManifest> manifest;
- static AndroidSdk ForMinSdk(std::string min_sdk) {
+ static AndroidSdk ForMinSdk(int min_sdk) {
AndroidSdk sdk;
- sdk.min_sdk_version = {std::move(min_sdk)};
+ sdk.min_sdk_version = min_sdk;
return sdk;
}
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index 7ffb3d5..3654901 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -24,6 +24,15 @@
#include "xml/XmlDom.h"
namespace aapt {
+
+namespace configuration {
+void PrintTo(const AndroidSdk& sdk, std::ostream* os) {
+ *os << "SDK: min=" << sdk.min_sdk_version.value_or_default(-1)
+ << ", target=" << sdk.target_sdk_version.value_or_default(-1)
+ << ", max=" << sdk.max_sdk_version.value_or_default(-1);
+}
+} // namespace configuration
+
namespace {
using ::android::ResTable_config;
@@ -76,9 +85,9 @@
</locale-group>
<android-sdk-group label="v19">
<android-sdk
- minSdkVersion="v19"
- targetSdkVersion="v24"
- maxSdkVersion="v25">
+ minSdkVersion="19"
+ targetSdkVersion="24"
+ maxSdkVersion="25">
<manifest>
<!--- manifest additions here XSLT? TODO -->
</manifest>
@@ -156,7 +165,7 @@
EXPECT_EQ(1ul, value.android_sdk_groups.size());
EXPECT_TRUE(value.android_sdk_groups["v19"].min_sdk_version);
- EXPECT_EQ("v19", value.android_sdk_groups["v19"].min_sdk_version.value());
+ EXPECT_EQ(19, value.android_sdk_groups["v19"].min_sdk_version.value());
EXPECT_EQ(1ul, value.gl_texture_groups.size());
EXPECT_EQ(1ul, value.gl_texture_groups["dxt1"].size());
@@ -174,55 +183,117 @@
}
TEST_F(ConfigurationParserTest, ArtifactAction) {
- static constexpr const char* xml = R"xml(
+ PostProcessingConfiguration config;
+ {
+ const auto doc = test::BuildXmlDom(R"xml(
+ <artifact
+ abi-group="arm"
+ screen-density-group="large"
+ locale-group="europe"
+ android-sdk-group="v19"
+ gl-texture-group="dxt1"
+ device-feature-group="low-latency"/>)xml");
+
+ ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc->root.get()), &diag_));
+
+ EXPECT_EQ(1ul, config.artifacts.size());
+
+ auto& artifact = config.artifacts.back();
+ EXPECT_FALSE(artifact.name); // TODO: make this fail.
+ EXPECT_EQ(1, artifact.version);
+ EXPECT_EQ("arm", artifact.abi_group.value());
+ EXPECT_EQ("large", artifact.screen_density_group.value());
+ EXPECT_EQ("europe", artifact.locale_group.value());
+ EXPECT_EQ("v19", artifact.android_sdk_group.value());
+ EXPECT_EQ("dxt1", artifact.gl_texture_group.value());
+ EXPECT_EQ("low-latency", artifact.device_feature_group.value());
+ }
+
+ {
+ // Perform a second action to ensure we get 2 artifacts.
+ const auto doc = test::BuildXmlDom(R"xml(
+ <artifact
+ abi-group="other"
+ screen-density-group="large"
+ locale-group="europe"
+ android-sdk-group="v19"
+ gl-texture-group="dxt1"
+ device-feature-group="low-latency"/>)xml");
+
+ ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
+ EXPECT_EQ(2ul, config.artifacts.size());
+ EXPECT_EQ(2, config.artifacts.back().version);
+ }
+
+ {
+ // Perform a third action with a set version code.
+ const auto doc = test::BuildXmlDom(R"xml(
<artifact
- abi-group="arm"
+ version="5"
+ abi-group="other"
screen-density-group="large"
locale-group="europe"
android-sdk-group="v19"
gl-texture-group="dxt1"
- device-feature-group="low-latency"/>)xml";
+ device-feature-group="low-latency"/>)xml");
- auto doc = test::BuildXmlDom(xml);
+ ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
+ EXPECT_EQ(3ul, config.artifacts.size());
+ EXPECT_EQ(5, config.artifacts.back().version);
+ }
- PostProcessingConfiguration config;
- bool ok = artifact_handler_(&config, NodeCast<Element>(doc->root.get()), &diag_);
- ASSERT_TRUE(ok);
-
- EXPECT_EQ(1ul, config.artifacts.size());
-
- auto& artifact = config.artifacts.front();
- EXPECT_FALSE(artifact.name); // TODO: make this fail.
- EXPECT_EQ("arm", artifact.abi_group.value());
- EXPECT_EQ("large", artifact.screen_density_group.value());
- EXPECT_EQ("europe", artifact.locale_group.value());
- EXPECT_EQ("v19", artifact.android_sdk_group.value());
- EXPECT_EQ("dxt1", artifact.gl_texture_group.value());
- EXPECT_EQ("low-latency", artifact.device_feature_group.value());
-
- // Perform a second action to ensure we get 2 artifacts.
- static constexpr const char* second = R"xml(
+ {
+ // Perform a fourth action to ensure the version code still increments.
+ const auto doc = test::BuildXmlDom(R"xml(
<artifact
abi-group="other"
screen-density-group="large"
locale-group="europe"
android-sdk-group="v19"
gl-texture-group="dxt1"
- device-feature-group="low-latency"/>)xml";
- doc = test::BuildXmlDom(second);
+ device-feature-group="low-latency"/>)xml");
- ok = artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
- ASSERT_TRUE(ok);
- EXPECT_EQ(2ul, config.artifacts.size());
+ ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
+ EXPECT_EQ(4ul, config.artifacts.size());
+ EXPECT_EQ(6, config.artifacts.back().version);
+ }
+}
+
+TEST_F(ConfigurationParserTest, DuplicateArtifactVersion) {
+ static constexpr const char* configuration = R"xml(<?xml version="1.0" encoding="utf-8" ?>
+ <pst-process xmlns="http://schemas.android.com/tools/aapt">>
+ <artifacts>
+ <artifact-format>
+ ${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release
+ </artifact-format>
+ <artifact
+ name="art1"
+ abi-group="arm"
+ screen-density-group="large"
+ locale-group="europe"
+ android-sdk-group="v19"
+ gl-texture-group="dxt1"
+ device-feature-group="low-latency"/>
+ <artifact
+ name="art2"
+ version = "1"
+ abi-group="other"
+ screen-density-group="alldpi"
+ locale-group="north-america"
+ android-sdk-group="v19"
+ gl-texture-group="dxt1"
+ device-feature-group="low-latency"/>
+ </artifacts>
+ </post-process>)xml";
+ auto result = ConfigurationParser::ForContents(configuration).Parse();
+ ASSERT_FALSE(result);
}
TEST_F(ConfigurationParserTest, ArtifactFormatAction) {
- static constexpr const char* xml = R"xml(
+ const auto doc = test::BuildXmlDom(R"xml(
<artifact-format>
${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release
- </artifact-format>)xml";
-
- auto doc = test::BuildXmlDom(xml);
+ </artifact-format>)xml");
PostProcessingConfiguration config;
bool ok = artifact_format_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
@@ -321,9 +392,9 @@
static constexpr const char* xml = R"xml(
<android-sdk-group label="v19">
<android-sdk
- minSdkVersion="v19"
- targetSdkVersion="v24"
- maxSdkVersion="v25">
+ minSdkVersion="19"
+ targetSdkVersion="24"
+ maxSdkVersion="25">
<manifest>
<!--- manifest additions here XSLT? TODO -->
</manifest>
@@ -342,14 +413,43 @@
auto& out = config.android_sdk_groups["v19"];
AndroidSdk sdk;
- sdk.min_sdk_version = std::string("v19");
- sdk.target_sdk_version = std::string("v24");
- sdk.max_sdk_version = std::string("v25");
+ sdk.min_sdk_version = 19;
+ sdk.target_sdk_version = 24;
+ sdk.max_sdk_version = 25;
sdk.manifest = AndroidManifest();
ASSERT_EQ(sdk, out);
}
+TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) {
+ static constexpr const char* xml = R"xml(
+ <android-sdk-group label="O">
+ <android-sdk
+ minSdkVersion="M"
+ targetSdkVersion="O"
+ maxSdkVersion="O">
+ </android-sdk>
+ </android-sdk-group>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = android_sdk_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_TRUE(ok);
+
+ ASSERT_EQ(1ul, config.android_sdk_groups.size());
+ ASSERT_EQ(1u, config.android_sdk_groups.count("O"));
+
+ auto& out = config.android_sdk_groups["O"];
+
+ AndroidSdk sdk;
+ sdk.min_sdk_version = {}; // Only the latest development version is supported.
+ sdk.target_sdk_version = 26;
+ sdk.max_sdk_version = 26;
+
+ ASSERT_EQ(sdk, out);
+}
+
TEST_F(ConfigurationParserTest, GlTextureGroupAction) {
static constexpr const char* xml = R"xml(
<gl-texture-group label="dxt1">
diff --git a/tools/aapt2/configuration/aapt2.xsd b/tools/aapt2/configuration/aapt2.xsd
index 47bf99e..134153a 100644
--- a/tools/aapt2/configuration/aapt2.xsd
+++ b/tools/aapt2/configuration/aapt2.xsd
@@ -39,6 +39,7 @@
<!-- Groups output artifacts together by dimension labels. -->
<xsd:complexType name="artifact">
<xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="version" type="xsd:integer"/>
<xsd:attribute name="abi-group" type="xsd:string"/>
<xsd:attribute name="android-sdk-group" type="xsd:string"/>
<xsd:attribute name="device-feature-group" type="xsd:string"/>
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index 5ff8908..8e4b82c 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -22,22 +22,50 @@
#include "androidfw/StringPiece.h"
#include "LoadedApk.h"
+#include "ResourceUtils.h"
+#include "ValueVisitor.h"
#include "configuration/ConfigurationParser.h"
#include "filter/AbiFilter.h"
#include "filter/Filter.h"
#include "flatten/Archive.h"
+#include "flatten/XmlFlattener.h"
#include "optimize/VersionCollapser.h"
#include "process/IResourceTableConsumer.h"
#include "split/TableSplitter.h"
#include "util/Files.h"
+#include "xml/XmlDom.h"
+#include "xml/XmlUtil.h"
namespace aapt {
using ::aapt::configuration::AndroidSdk;
using ::aapt::configuration::Artifact;
using ::aapt::configuration::PostProcessingConfiguration;
+using ::aapt::xml::kSchemaAndroid;
+using ::aapt::xml::XmlResource;
using ::android::StringPiece;
+namespace {
+
+Maybe<AndroidSdk> GetAndroidSdk(const Artifact& artifact, const PostProcessingConfiguration& config,
+ IDiagnostics* diag) {
+ if (!artifact.android_sdk_group) {
+ return {};
+ }
+
+ const std::string& group_name = artifact.android_sdk_group.value();
+ auto group = config.android_sdk_groups.find(group_name);
+ // TODO: Remove validation when configuration parser ensures referential integrity.
+ if (group == config.android_sdk_groups.end()) {
+ diag->Error(DiagMessage() << "could not find referenced group '" << group_name << "'");
+ return {};
+ }
+
+ return group->second;
+}
+
+} // namespace
+
/**
* Context wrapper that allows the min Android SDK value to be overridden.
*/
@@ -127,6 +155,13 @@
return false;
}
+ std::unique_ptr<XmlResource> manifest;
+ if (!UpdateManifest(artifact, config, &manifest, diag)) {
+ diag->Error(DiagMessage() << "could not update AndroidManifest.xml for "
+ << artifact_name.value());
+ return false;
+ }
+
std::string out = options.out_dir;
if (!file::mkdirs(out)) {
context_->GetDiagnostics()->Warn(DiagMessage() << "could not create out dir: " << out);
@@ -145,7 +180,7 @@
}
if (!apk_->WriteToArchive(context_, table.get(), options.table_flattener_options, &filters,
- writer.get())) {
+ writer.get(), manifest.get())) {
return false;
}
}
@@ -208,37 +243,15 @@
splits.config_filter = &axis_filter;
}
- if (artifact.android_sdk_group) {
- const std::string& group_name = artifact.android_sdk_group.value();
- auto group = config.android_sdk_groups.find(group_name);
- // TODO: Remove validation when configuration parser ensures referential integrity.
- if (group == config.android_sdk_groups.end()) {
- context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '"
- << group_name << "'");
- return {};
- }
-
- const AndroidSdk& sdk = group->second;
- if (!sdk.min_sdk_version) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "skipping SDK version. No min SDK: " << group_name);
- return {};
- }
-
- ConfigDescription c;
- const std::string& version = sdk.min_sdk_version.value();
- if (!ConfigDescription::Parse(version, &c)) {
- context_->GetDiagnostics()->Error(DiagMessage() << "could not parse min SDK: " << version);
- return {};
- }
-
- wrappedContext.SetMinSdkVersion(c.sdkVersion);
+ Maybe<AndroidSdk> sdk = GetAndroidSdk(artifact, config, context_->GetDiagnostics());
+ if (sdk && sdk.value().min_sdk_version) {
+ wrappedContext.SetMinSdkVersion(sdk.value().min_sdk_version.value());
}
std::unique_ptr<ResourceTable> table = old_table.Clone();
VersionCollapser collapser;
- if (!collapser.Consume(context_, table.get())) {
+ if (!collapser.Consume(&wrappedContext, table.get())) {
context_->GetDiagnostics()->Error(DiagMessage() << "Failed to strip versioned resources");
return {};
}
@@ -248,4 +261,95 @@
return table;
}
+bool MultiApkGenerator::UpdateManifest(const Artifact& artifact,
+ const PostProcessingConfiguration& config,
+ std::unique_ptr<XmlResource>* updated_manifest,
+ IDiagnostics* diag) {
+ *updated_manifest = apk_->InflateManifest(context_);
+ XmlResource* manifest = updated_manifest->get();
+ if (manifest == nullptr) {
+ return false;
+ }
+
+ // Make sure the first element is <manifest> with package attribute.
+ xml::Element* manifest_el = manifest->root.get();
+ if (manifest_el == nullptr) {
+ return false;
+ }
+
+ if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") {
+ diag->Error(DiagMessage(manifest->file.source) << "root tag must be <manifest>");
+ return false;
+ }
+
+ // Update the versionCode attribute.
+ xml::Attribute* versionCode = manifest_el->FindAttribute(kSchemaAndroid, "versionCode");
+ if (versionCode == nullptr) {
+ diag->Error(DiagMessage(manifest->file.source) << "manifest must have a versionCode attribute");
+ return false;
+ }
+
+ auto* compiled_version = ValueCast<BinaryPrimitive>(versionCode->compiled_value.get());
+ if (compiled_version == nullptr) {
+ diag->Error(DiagMessage(manifest->file.source) << "versionCode is invalid");
+ return false;
+ }
+
+ int new_version = compiled_version->value.data + artifact.version;
+ versionCode->compiled_value = ResourceUtils::TryParseInt(std::to_string(new_version));
+
+ // Check to see if the minSdkVersion needs to be updated.
+ Maybe<AndroidSdk> maybe_sdk = GetAndroidSdk(artifact, config, diag);
+ if (maybe_sdk) {
+ // TODO(safarmer): Handle the rest of the Android SDK.
+ const AndroidSdk& android_sdk = maybe_sdk.value();
+
+ if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
+ if (xml::Attribute* min_sdk_attr =
+ uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
+ // Populate with a pre-compiles attribute to we don't need to relink etc.
+ const std::string& min_sdk_str = std::to_string(android_sdk.min_sdk_version.value());
+ min_sdk_attr->compiled_value = ResourceUtils::TryParseInt(min_sdk_str);
+ } else {
+ // There was no minSdkVersion. This is strange since at this point we should have been
+ // through the manifest fixer which sets the default minSdkVersion.
+ diag->Error(DiagMessage(manifest->file.source) << "missing minSdkVersion from <uses-sdk>");
+ return false;
+ }
+ } else {
+ // No uses-sdk present. This is strange since at this point we should have been
+ // through the manifest fixer which should have added it.
+ diag->Error(DiagMessage(manifest->file.source) << "missing <uses-sdk> from <manifest>");
+ return false;
+ }
+ }
+
+ if (artifact.screen_density_group) {
+ auto densities = config.screen_density_groups.find(artifact.screen_density_group.value());
+ CHECK(densities != config.screen_density_groups.end()) << "Missing density group";
+
+ xml::Element* screens_el = manifest_el->FindChild({}, "compatible-screens");
+ if (!screens_el) {
+ // create a new element.
+ std::unique_ptr<xml::Element> new_screens_el = util::make_unique<xml::Element>();
+ new_screens_el->name = "compatible-screens";
+ screens_el = new_screens_el.get();
+ manifest_el->InsertChild(0, std::move(new_screens_el));
+ } else {
+ // clear out the old element.
+ screens_el->GetChildElements().clear();
+ }
+
+ for (const auto& density : densities->second) {
+ std::unique_ptr<xml::Element> screen_el = util::make_unique<xml::Element>();
+ screen_el->name = "screen";
+ const char* density_str = density.toString().string();
+ screen_el->attributes.push_back(xml::Attribute{kSchemaAndroid, "screenDensity", density_str});
+ screens_el->AppendChild(std::move(screen_el));
+ }
+ }
+
+ return true;
+}
+
} // namespace aapt
diff --git a/tools/aapt2/optimize/MultiApkGenerator.h b/tools/aapt2/optimize/MultiApkGenerator.h
index b064400..e6546ee 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.h
+++ b/tools/aapt2/optimize/MultiApkGenerator.h
@@ -54,6 +54,10 @@
return context_->GetDiagnostics();
}
+ bool UpdateManifest(const configuration::Artifact& artifact,
+ const configuration::PostProcessingConfiguration& config,
+ std::unique_ptr<xml::XmlResource>* updated_manifest, IDiagnostics* diag);
+
LoadedApk* apk_;
IAaptContext* context_;
};
diff --git a/tools/aapt2/optimize/MultiApkGenerator_test.cpp b/tools/aapt2/optimize/MultiApkGenerator_test.cpp
index 23f573c..e8e6adc 100644
--- a/tools/aapt2/optimize/MultiApkGenerator_test.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator_test.cpp
@@ -50,14 +50,6 @@
using ::testing::Test;
using ::testing::_;
-/** Subclass the LoadedApk class so that we can mock the WriteToArchive method. */
-class MockApk : public LoadedApk {
- public:
- MockApk(std::unique_ptr<ResourceTable> table) : LoadedApk({"test.apk"}, {}, std::move(table)){};
- MOCK_METHOD5(WriteToArchive, bool(IAaptContext*, ResourceTable*, const TableFlattenerOptions&,
- FilterChain*, IArchiveWriter*));
-};
-
/**
* Subclass the MultiApkGenerator class so that we can access the protected FilterTable method to
* directly test table filter.
@@ -111,54 +103,10 @@
ConfigDescription v21_ = ParseConfigOrDie("v21");
};
-TEST_F(MultiApkGeneratorTest, FromBaseApk) {
- std::unique_ptr<ResourceTable> table = BuildTable();
-
- MockApk apk{std::move(table)};
-
- EXPECT_CALL(apk, WriteToArchive(_, _, _, _, _)).Times(0);
-
- test::Context ctx;
- PostProcessingConfiguration empty_config;
- TableFlattenerOptions table_flattener_options;
-
- MultiApkGenerator generator{&apk, &ctx};
- EXPECT_TRUE(generator.FromBaseApk({"out", empty_config, table_flattener_options}));
-
- Artifact x64 = test::ArtifactBuilder()
- .SetName("${basename}.x64.apk")
- .SetAbiGroup("x64")
- .SetLocaleGroup("en")
- .SetDensityGroup("xhdpi")
- .Build();
-
- Artifact intel = test::ArtifactBuilder()
- .SetName("${basename}.intel.apk")
- .SetAbiGroup("intel")
- .SetLocaleGroup("europe")
- .SetDensityGroup("large")
- .Build();
-
- auto config = test::PostProcessingConfigurationBuilder()
- .SetLocaleGroup("en", {"en"})
- .SetLocaleGroup("europe", {"en", "fr", "de", "es"})
- .SetAbiGroup("x64", {Abi::kX86_64})
- .SetAbiGroup("intel", {Abi::kX86_64, Abi::kX86})
- .SetDensityGroup("xhdpi", {"xhdpi"})
- .SetDensityGroup("large", {"xhdpi", "xxhdpi", "xxxhdpi"})
- .AddArtifact(x64)
- .AddArtifact(intel)
- .Build();
-
- // Called once for each artifact.
- EXPECT_CALL(apk, WriteToArchive(Eq(&ctx), _, _, _, _)).Times(2).WillRepeatedly(Return(true));
- EXPECT_TRUE(generator.FromBaseApk({"out", config, table_flattener_options}));
-}
-
TEST_F(MultiApkGeneratorTest, VersionFilterNewerVersion) {
std::unique_ptr<ResourceTable> table = BuildTable();
- MockApk apk{std::move(table)};
+ LoadedApk apk = {{"test.apk"}, {}, std::move(table)};
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(19).Build();
PostProcessingConfiguration empty_config;
TableFlattenerOptions table_flattener_options;
@@ -174,7 +122,7 @@
.SetLocaleGroup("en", {"en"})
.SetAbiGroup("x64", {Abi::kX86_64})
.SetDensityGroup("xhdpi", {"xhdpi"})
- .SetAndroidSdk("v23", AndroidSdk::ForMinSdk("v23"))
+ .SetAndroidSdk("v23", AndroidSdk::ForMinSdk(23))
.AddArtifact(x64)
.Build();
@@ -199,7 +147,7 @@
TEST_F(MultiApkGeneratorTest, VersionFilterOlderVersion) {
std::unique_ptr<ResourceTable> table = BuildTable();
- MockApk apk{std::move(table)};
+ LoadedApk apk = {{"test.apk"}, {}, std::move(table)};
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
PostProcessingConfiguration empty_config;
TableFlattenerOptions table_flattener_options;
@@ -215,7 +163,7 @@
.SetLocaleGroup("en", {"en"})
.SetAbiGroup("x64", {Abi::kX86_64})
.SetDensityGroup("xhdpi", {"xhdpi"})
- .SetAndroidSdk("v4", AndroidSdk::ForMinSdk("v4"))
+ .SetAndroidSdk("v4", AndroidSdk::ForMinSdk(4))
.AddArtifact(x64)
.Build();
@@ -238,7 +186,7 @@
TEST_F(MultiApkGeneratorTest, VersionFilterNoVersion) {
std::unique_ptr<ResourceTable> table = BuildTable();
- MockApk apk{std::move(table)};
+ LoadedApk apk = {{"test.apk"}, {}, std::move(table)};
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
PostProcessingConfiguration empty_config;
TableFlattenerOptions table_flattener_options;
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index 10a4587..6b97ae6 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -18,6 +18,7 @@
#include <string>
+#include "flatten/XmlFlattener.h"
#include "io/StringInputStream.h"
#include "test/Test.h"
@@ -51,6 +52,36 @@
EXPECT_THAT(el->namespace_decls[0].prefix, StrEq("android"));
}
+TEST(XmlDomTest, BinaryInflate) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<XmlResource> doc = util::make_unique<XmlResource>();
+ doc->root = util::make_unique<Element>();
+ doc->root->name = "Layout";
+ doc->root->line_number = 2u;
+
+ NamespaceDecl decl;
+ decl.uri = kSchemaAndroid;
+ decl.prefix = "android";
+ decl.line_number = 2u;
+ doc->root->namespace_decls.push_back(decl);
+
+ BigBuffer buffer(4096);
+ XmlFlattener flattener(&buffer, {});
+ ASSERT_TRUE(flattener.Consume(context.get(), doc.get()));
+
+ auto block = util::Copy(buffer);
+ std::unique_ptr<XmlResource> new_doc =
+ Inflate(block.get(), buffer.size(), context->GetDiagnostics(), Source("test.xml"));
+ ASSERT_THAT(new_doc, NotNull());
+
+ EXPECT_THAT(new_doc->root->name, StrEq("Layout"));
+ EXPECT_THAT(new_doc->root->line_number, Eq(2u));
+ ASSERT_THAT(new_doc->root->namespace_decls, SizeIs(1u));
+ EXPECT_THAT(new_doc->root->namespace_decls[0].uri, StrEq(kSchemaAndroid));
+ EXPECT_THAT(new_doc->root->namespace_decls[0].prefix, StrEq("android"));
+ EXPECT_THAT(new_doc->root->namespace_decls[0].line_number, Eq(2u));
+}
+
// Escaping is handled after parsing of the values for resource-specific values.
TEST(XmlDomTest, ForwardEscapes) {
std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"(
diff --git a/wifi/java/android/net/wifi/rtt/IRttCallback.aidl b/wifi/java/android/net/wifi/rtt/IRttCallback.aidl
new file mode 100644
index 0000000..fb1636f
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/IRttCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.net.wifi.rtt;
+
+import android.net.wifi.rtt.RangingResult;
+
+/**
+ * Interface for RTT result callback.
+ *
+ * @hide
+ */
+oneway interface IRttCallback
+{
+ /**
+ * Service to manager callback providing RTT status and results.
+ */
+ void onRangingResults(int status, in List<RangingResult> results);
+}
diff --git a/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl b/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl
new file mode 100644
index 0000000..ad92e04
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.net.wifi.rtt;
+
+import android.net.wifi.rtt.IRttCallback;
+import android.net.wifi.rtt.RangingRequest;
+
+/**
+ * @hide
+ */
+interface IWifiRttManager
+{
+ void startRanging(in IBinder binder, in String callingPackage, in RangingRequest request,
+ in IRttCallback callback);
+}
diff --git a/wifi/java/android/net/wifi/rtt/RangingRequest.aidl b/wifi/java/android/net/wifi/rtt/RangingRequest.aidl
new file mode 100644
index 0000000..8053c94
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/RangingRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.net.wifi.rtt;
+
+parcelable RangingRequest;
diff --git a/wifi/java/android/net/wifi/rtt/RangingRequest.java b/wifi/java/android/net/wifi/rtt/RangingRequest.java
new file mode 100644
index 0000000..997b680
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/RangingRequest.java
@@ -0,0 +1,237 @@
+/*
+ * 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.net.wifi.rtt;
+
+import android.net.wifi.ScanResult;
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+ * Defines the ranging request to other devices. The ranging request is built using
+ * {@link RangingRequest.Builder}.
+ * A ranging request is executed using
+ * {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)}.
+ * <p>
+ * The ranging request is a batch request - specifying a set of devices (specified using
+ * {@link RangingRequest.Builder#addAp(ScanResult)} and
+ * {@link RangingRequest.Builder#addAps(List)}).
+ *
+ * @hide RTT_API
+ */
+public final class RangingRequest implements Parcelable {
+ private static final int MAX_PEERS = 10;
+
+ /**
+ * Returns the maximum number of peers to range which can be specified in a single {@code
+ * RangingRequest}. The limit applies no matter how the peers are added to the request, e.g.
+ * through {@link RangingRequest.Builder#addAp(ScanResult)} or
+ * {@link RangingRequest.Builder#addAps(List)}.
+ *
+ * @return Maximum number of peers.
+ */
+ public static int getMaxPeers() {
+ return MAX_PEERS;
+ }
+
+ /** @hide */
+ public final List<RttPeer> mRttPeers;
+
+ /** @hide */
+ private RangingRequest(List<RttPeer> rttPeers) {
+ mRttPeers = rttPeers;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeList(mRttPeers);
+ }
+
+ public static final Creator<RangingRequest> CREATOR = new Creator<RangingRequest>() {
+ @Override
+ public RangingRequest[] newArray(int size) {
+ return new RangingRequest[size];
+ }
+
+ @Override
+ public RangingRequest createFromParcel(Parcel in) {
+ return new RangingRequest(in.readArrayList(null));
+ }
+ };
+
+ /** @hide */
+ @Override
+ public String toString() {
+ StringJoiner sj = new StringJoiner(", ", "RangingRequest: mRttPeers=[", ",");
+ for (RttPeer rp : mRttPeers) {
+ sj.add(rp.toString());
+ }
+ return sj.toString();
+ }
+
+ /** @hide */
+ public void enforceValidity() {
+ if (mRttPeers.size() > MAX_PEERS) {
+ throw new IllegalArgumentException(
+ "Ranging to too many peers requested. Use getMaxPeers() API to get limit.");
+ }
+ }
+
+ /**
+ * Builder class used to construct {@link RangingRequest} objects.
+ */
+ public static final class Builder {
+ private List<RttPeer> mRttPeers = new ArrayList<>();
+
+ /**
+ * Add the device specified by the {@link ScanResult} to the list of devices with
+ * which to measure range. The total number of results added to a request cannot exceed the
+ * limit specified by {@link #getMaxPeers()}.
+ *
+ * @param apInfo Information of an Access Point (AP) obtained in a Scan Result.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder addAp(ScanResult apInfo) {
+ if (apInfo == null) {
+ throw new IllegalArgumentException("Null ScanResult!");
+ }
+ mRttPeers.add(new RttPeerAp(apInfo));
+ return this;
+ }
+
+ /**
+ * Add the devices specified by the {@link ScanResult}s to the list of devices with
+ * which to measure range. The total number of results added to a request cannot exceed the
+ * limit specified by {@link #getMaxPeers()}.
+ *
+ * @param apInfos Information of an Access Points (APs) obtained in a Scan Result.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder addAps(List<ScanResult> apInfos) {
+ if (apInfos == null) {
+ throw new IllegalArgumentException("Null list of ScanResults!");
+ }
+ for (ScanResult scanResult : apInfos) {
+ addAp(scanResult);
+ }
+ return this;
+ }
+
+ /**
+ * Build {@link RangingRequest} given the current configurations made on the
+ * builder.
+ */
+ public RangingRequest build() {
+ return new RangingRequest(mRttPeers);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof RangingRequest)) {
+ return false;
+ }
+
+ RangingRequest lhs = (RangingRequest) o;
+
+ return mRttPeers.size() == lhs.mRttPeers.size() && mRttPeers.containsAll(lhs.mRttPeers);
+ }
+
+ @Override
+ public int hashCode() {
+ return mRttPeers.hashCode();
+ }
+
+ /** @hide */
+ public interface RttPeer {
+ // empty (marker interface)
+ }
+
+ /** @hide */
+ public static class RttPeerAp implements RttPeer, Parcelable {
+ public final ScanResult scanResult;
+
+ public RttPeerAp(ScanResult scanResult) {
+ this.scanResult = scanResult;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ scanResult.writeToParcel(dest, flags);
+ }
+
+ public static final Creator<RttPeerAp> CREATOR = new Creator<RttPeerAp>() {
+ @Override
+ public RttPeerAp[] newArray(int size) {
+ return new RttPeerAp[size];
+ }
+
+ @Override
+ public RttPeerAp createFromParcel(Parcel in) {
+ return new RttPeerAp(ScanResult.CREATOR.createFromParcel(in));
+ }
+ };
+
+ @Override
+ public String toString() {
+ return new StringBuilder("RttPeerAp: scanResult=").append(
+ scanResult.toString()).toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof RttPeerAp)) {
+ return false;
+ }
+
+ RttPeerAp lhs = (RttPeerAp) o;
+
+ // Note: the only thing which matters for the request identity is the BSSID of the AP
+ return TextUtils.equals(scanResult.BSSID, lhs.scanResult.BSSID);
+ }
+
+ @Override
+ public int hashCode() {
+ return scanResult.hashCode();
+ }
+ }
+}
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.aidl b/wifi/java/android/net/wifi/rtt/RangingResult.aidl
new file mode 100644
index 0000000..ae295a6
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.net.wifi.rtt;
+
+parcelable RangingResult;
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java
new file mode 100644
index 0000000..918803e
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -0,0 +1,194 @@
+/*
+ * 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.net.wifi.rtt;
+
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import libcore.util.HexEncoding;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Ranging result for a request started by
+ * {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)}. Results are
+ * returned in {@link RangingResultCallback#onRangingResults(List)}.
+ * <p>
+ * A ranging result is the distance measurement result for a single device specified in the
+ * {@link RangingRequest}.
+ *
+ * @hide RTT_API
+ */
+public final class RangingResult implements Parcelable {
+ private static final String TAG = "RangingResult";
+
+ private final int mStatus;
+ private final byte[] mMac;
+ private final int mDistanceCm;
+ private final int mDistanceStdDevCm;
+ private final int mRssi;
+ private final long mTimestamp;
+
+ /** @hide */
+ public RangingResult(int status, byte[] mac, int distanceCm, int distanceStdDevCm, int rssi,
+ long timestamp) {
+ mStatus = status;
+ mMac = mac;
+ mDistanceCm = distanceCm;
+ mDistanceStdDevCm = distanceStdDevCm;
+ mRssi = rssi;
+ mTimestamp = timestamp;
+ }
+
+ /**
+ * @return The status of ranging measurement: {@link RangingResultCallback#STATUS_SUCCESS} in
+ * case of success, and {@link RangingResultCallback#STATUS_FAIL} in case of failure.
+ */
+ public int getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * @return The MAC address of the device whose range measurement was requested. Will correspond
+ * to the MAC address of the device in the {@link RangingRequest}.
+ * <p>
+ * Always valid (i.e. when {@link #getStatus()} is either SUCCESS or FAIL.
+ */
+ public byte[] getMacAddress() {
+ return mMac;
+ }
+
+ /**
+ * @return The distance (in cm) to the device specified by {@link #getMacAddress()}.
+ * <p>
+ * Only valid if {@link #getStatus()} returns {@link RangingResultCallback#STATUS_SUCCESS}.
+ */
+ public int getDistanceCm() {
+ if (mStatus != RangingResultCallback.STATUS_SUCCESS) {
+ Log.e(TAG, "getDistanceCm(): invalid value retrieved");
+ }
+ return mDistanceCm;
+ }
+
+ /**
+ * @return The standard deviation of the measured distance (in cm) to the device specified by
+ * {@link #getMacAddress()}. The standard deviation is calculated over the measurements
+ * executed in a single RTT burst.
+ * <p>
+ * Only valid if {@link #getStatus()} returns {@link RangingResultCallback#STATUS_SUCCESS}.
+ */
+ public int getDistanceStdDevCm() {
+ if (mStatus != RangingResultCallback.STATUS_SUCCESS) {
+ Log.e(TAG, "getDistanceStdDevCm(): invalid value retrieved");
+ }
+ return mDistanceStdDevCm;
+ }
+
+ /**
+ * @return The average RSSI (in units of -0.5dB) observed during the RTT measurement.
+ * <p>
+ * Only valid if {@link #getStatus()} returns {@link RangingResultCallback#STATUS_SUCCESS}.
+ */
+ public int getRssi() {
+ if (mStatus != RangingResultCallback.STATUS_SUCCESS) {
+ // TODO: should this be an exception?
+ Log.e(TAG, "getRssi(): invalid value retrieved");
+ }
+ return mRssi;
+ }
+
+ /**
+ * @return The timestamp (in us) at which the ranging operation was performed
+ * <p>
+ * Only valid if {@link #getStatus()} returns {@link RangingResultCallback#STATUS_SUCCESS}.
+ */
+ public long getRangingTimestamp() {
+ return mTimestamp;
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mStatus);
+ dest.writeByteArray(mMac);
+ dest.writeInt(mDistanceCm);
+ dest.writeInt(mDistanceStdDevCm);
+ dest.writeInt(mRssi);
+ dest.writeLong(mTimestamp);
+ }
+
+ /** @hide */
+ public static final Creator<RangingResult> CREATOR = new Creator<RangingResult>() {
+ @Override
+ public RangingResult[] newArray(int size) {
+ return new RangingResult[size];
+ }
+
+ @Override
+ public RangingResult createFromParcel(Parcel in) {
+ int status = in.readInt();
+ byte[] mac = in.createByteArray();
+ int distanceCm = in.readInt();
+ int distanceStdDevCm = in.readInt();
+ int rssi = in.readInt();
+ long timestamp = in.readLong();
+ return new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi, timestamp);
+ }
+ };
+
+ /** @hide */
+ @Override
+ public String toString() {
+ return new StringBuilder("RangingResult: [status=").append(mStatus).append(", mac=").append(
+ mMac == null ? "<null>" : HexEncoding.encodeToString(mMac)).append(
+ ", distanceCm=").append(mDistanceCm).append(", distanceStdDevCm=").append(
+ mDistanceStdDevCm).append(", rssi=").append(mRssi).append(", timestamp=").append(
+ mTimestamp).append("]").toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof RangingResult)) {
+ return false;
+ }
+
+ RangingResult lhs = (RangingResult) o;
+
+ return mStatus == lhs.mStatus && Arrays.equals(mMac, lhs.mMac)
+ && mDistanceCm == lhs.mDistanceCm && mDistanceStdDevCm == lhs.mDistanceStdDevCm
+ && mRssi == lhs.mRssi && mTimestamp == lhs.mTimestamp;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mStatus, mMac, mDistanceCm, mDistanceStdDevCm, mRssi, mTimestamp);
+ }
+}
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/rtt/RangingResultCallback.java b/wifi/java/android/net/wifi/rtt/RangingResultCallback.java
new file mode 100644
index 0000000..d7270ad
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/RangingResultCallback.java
@@ -0,0 +1,56 @@
+/*
+ * 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.net.wifi.rtt;
+
+import android.os.Handler;
+
+import java.util.List;
+
+/**
+ * Base class for ranging result callbacks. Should be extended by applications and set when calling
+ * {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)}. A single
+ * result from a range request will be called in this object.
+ *
+ * @hide RTT_API
+ */
+public abstract class RangingResultCallback {
+ /**
+ * Individual range request status, {@link RangingResult#getStatus()}. Indicates ranging
+ * operation was successful and distance value is valid.
+ */
+ public static final int STATUS_SUCCESS = 0;
+
+ /**
+ * Individual range request status, {@link RangingResult#getStatus()}. Indicates ranging
+ * operation failed and the distance value is invalid.
+ */
+ public static final int STATUS_FAIL = 1;
+
+ /**
+ * Called when a ranging operation failed in whole - i.e. no ranging operation to any of the
+ * devices specified in the request was attempted.
+ */
+ public abstract void onRangingFailure();
+
+ /**
+ * Called when a ranging operation was executed. The list of results corresponds to devices
+ * specified in the ranging request.
+ *
+ * @param results List of range measurements, one per requested device.
+ */
+ public abstract void onRangingResults(List<RangingResult> results);
+}
diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
new file mode 100644
index 0000000..a085de1
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
@@ -0,0 +1,100 @@
+package android.net.wifi.rtt;
+
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_WIFI_STATE;
+import static android.Manifest.permission.CHANGE_WIFI_STATE;
+
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * This class provides the primary API for measuring distance (range) to other devices using the
+ * IEEE 802.11mc Wi-Fi Round Trip Time (RTT) technology.
+ * <p>
+ * The devices which can be ranged include:
+ * <li>Access Points (APs)
+ * <p>
+ * Ranging requests are triggered using
+ * {@link #startRanging(RangingRequest, RangingResultCallback, Handler)}. Results (in case of
+ * successful operation) are returned in the {@link RangingResultCallback#onRangingResults(List)}
+ * callback.
+ *
+ * @hide RTT_API
+ */
+@SystemService(Context.WIFI_RTT2_SERVICE)
+public class WifiRttManager {
+ private static final String TAG = "WifiRttManager";
+ private static final boolean VDBG = true;
+
+ private final Context mContext;
+ private final IWifiRttManager mService;
+
+ /** @hide */
+ public WifiRttManager(Context context, IWifiRttManager service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Initiate a request to range to a set of devices specified in the {@link RangingRequest}.
+ * Results will be returned in the {@link RangingResultCallback} set of callbacks.
+ *
+ * @param request A request specifying a set of devices whose distance measurements are
+ * requested.
+ * @param callback A callback for the result of the ranging request.
+ * @param handler The Handler on whose thread to execute the callbacks of the {@code
+ * callback} object. If a null is provided then the application's main thread
+ * will be used.
+ */
+ @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, CHANGE_WIFI_STATE, ACCESS_WIFI_STATE})
+ public void startRanging(RangingRequest request, RangingResultCallback callback,
+ @Nullable Handler handler) {
+ if (VDBG) {
+ Log.v(TAG, "startRanging: request=" + request + ", callback=" + callback + ", handler="
+ + handler);
+ }
+
+ Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
+ Binder binder = new Binder();
+ try {
+ mService.startRanging(binder, mContext.getOpPackageName(), request,
+ new RttCallbackProxy(looper, callback));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private static class RttCallbackProxy extends IRttCallback.Stub {
+ private final Handler mHandler;
+ private final RangingResultCallback mCallback;
+
+ RttCallbackProxy(Looper looper, RangingResultCallback callback) {
+ mHandler = new Handler(looper);
+ mCallback = callback;
+ }
+
+ @Override
+ public void onRangingResults(int status, List<RangingResult> results) throws RemoteException {
+ if (VDBG) {
+ Log.v(TAG, "RttCallbackProxy: onRanginResults: status=" + status + ", results="
+ + results);
+ }
+ mHandler.post(() -> {
+ if (status == RangingResultCallback.STATUS_SUCCESS) {
+ mCallback.onRangingResults(results);
+ } else {
+ mCallback.onRangingFailure();
+ }
+ });
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/rtt/package.html b/wifi/java/android/net/wifi/rtt/package.html
new file mode 100644
index 0000000..221b94b
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/package.html
@@ -0,0 +1,39 @@
+<HTML>
+<BODY>
+<p>Provides classes which allow applications to use Wi-Fi RTT (IEEE 802.11mc) to measure distance
+ to supporting Access Points and peer devices.</p>
+<p>The primary entry point to Wi-Fi RTT capabilities is the
+ {@link android.net.wifi.rtt.WifiRttManager} class, which is acquired by calling
+ {@link android.content.Context#getSystemService(String)
+ Context.getSystemService(Context.WIFI_RTT_SERVICE)}</p>
+
+<p>Some APIs may require the following user permissions:</p>
+<ul>
+ <li>{@link android.Manifest.permission#ACCESS_WIFI_STATE}</li>
+ <li>{@link android.Manifest.permission#CHANGE_WIFI_STATE}</li>
+ <li>{@link android.Manifest.permission#ACCESS_COARSE_LOCATION}</li>
+</ul>
+
+<p class="note"><strong>Note:</strong> Not all Android-powered devices support Wi-Fi RTT
+ functionality.
+ If your application only works with Wi-Fi RTT (i.e. it should only be installed on devices which
+ support Wi-Fi RTT), declare so with a <a
+ href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
+ {@code <uses-feature>}</a>
+ element in the manifest file:</p>
+<pre>
+<manifest ...>
+ <uses-feature android:name="android.hardware.wifi.rtt" />
+ ...
+</manifest>
+</pre>
+<p>Alternatively, if your application does not require Wi-Fi RTT but can take advantage of it if
+ available, you can perform
+ the check at run-time in your code using {@link
+ android.content.pm.PackageManager#hasSystemFeature(String)} with {@link
+ android.content.pm.PackageManager#FEATURE_WIFI_RTT}:</p>
+<pre>
+ getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)
+</pre>
+</BODY>
+</HTML>
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
new file mode 100644
index 0000000..23c75ce
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -0,0 +1,226 @@
+/*
+ * 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.net.wifi.rtt;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.wifi.ScanResult;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import libcore.util.HexEncoding;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit test harness for WifiRttManager class.
+ */
+@SmallTest
+public class WifiRttManagerTest {
+ private WifiRttManager mDut;
+ private TestLooper mMockLooper;
+ private Handler mMockLooperHandler;
+
+ private final String packageName = "some.package.name.for.rtt.app";
+
+ @Mock
+ public Context mockContext;
+
+ @Mock
+ public IWifiRttManager mockRttService;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mDut = new WifiRttManager(mockContext, mockRttService);
+ mMockLooper = new TestLooper();
+ mMockLooperHandler = new Handler(mMockLooper.getLooper());
+
+ when(mockContext.getOpPackageName()).thenReturn(packageName);
+ }
+
+ /**
+ * Validate ranging call flow with succesful results.
+ */
+ @Test
+ public void testRangeSuccess() throws Exception {
+ RangingRequest request = new RangingRequest.Builder().build();
+ List<RangingResult> results = new ArrayList<>();
+ results.add(new RangingResult(RangingResultCallback.STATUS_SUCCESS, null, 15, 5, 10, 666));
+ RangingResultCallback callbackMock = mock(RangingResultCallback.class);
+ ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
+
+ // verify ranging request passed to service
+ mDut.startRanging(request, callbackMock, mMockLooperHandler);
+ verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(request),
+ callbackCaptor.capture());
+
+ // service calls back with success
+ callbackCaptor.getValue().onRangingResults(RangingResultCallback.STATUS_SUCCESS, results);
+ mMockLooper.dispatchAll();
+ verify(callbackMock).onRangingResults(results);
+
+ verifyNoMoreInteractions(mockRttService, callbackMock);
+ }
+
+ /**
+ * Validate ranging call flow which failed.
+ */
+ @Test
+ public void testRangeFail() throws Exception {
+ RangingRequest request = new RangingRequest.Builder().build();
+ RangingResultCallback callbackMock = mock(RangingResultCallback.class);
+ ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
+
+ // verify ranging request passed to service
+ mDut.startRanging(request, callbackMock, mMockLooperHandler);
+ verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(request),
+ callbackCaptor.capture());
+
+ // service calls back with failure code
+ callbackCaptor.getValue().onRangingResults(RangingResultCallback.STATUS_FAIL, null);
+ mMockLooper.dispatchAll();
+ verify(callbackMock).onRangingFailure();
+
+ verifyNoMoreInteractions(mockRttService, callbackMock);
+ }
+
+ /**
+ * Validate that RangingRequest parcel works (produces same object on write/read).
+ */
+ @Test
+ public void testRangingRequestParcel() {
+ // Note: not validating parcel code of ScanResult (assumed to work)
+ ScanResult scanResult1 = new ScanResult();
+ scanResult1.BSSID = "00:01:02:03:04:05";
+ ScanResult scanResult2 = new ScanResult();
+ scanResult2.BSSID = "06:07:08:09:0A:0B";
+ ScanResult scanResult3 = new ScanResult();
+ scanResult3.BSSID = "AA:BB:CC:DD:EE:FF";
+ List<ScanResult> scanResults2and3 = new ArrayList<>(2);
+ scanResults2and3.add(scanResult2);
+ scanResults2and3.add(scanResult3);
+
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ builder.addAp(scanResult1);
+ builder.addAps(scanResults2and3);
+ RangingRequest request = builder.build();
+
+ Parcel parcelW = Parcel.obtain();
+ request.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ RangingRequest rereadRequest = RangingRequest.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(request, rereadRequest);
+ }
+
+ /**
+ * Validate that can request as many range operation as the upper limit on number of requests.
+ */
+ @Test
+ public void testRangingRequestAtLimit() {
+ ScanResult scanResult = new ScanResult();
+ List<ScanResult> scanResultList = new ArrayList<>();
+ for (int i = 0; i < RangingRequest.getMaxPeers() - 2; ++i) {
+ scanResultList.add(scanResult);
+ }
+
+ // create request
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ builder.addAp(scanResult);
+ builder.addAps(scanResultList);
+ builder.addAp(scanResult);
+ RangingRequest request = builder.build();
+
+ // verify request
+ request.enforceValidity();
+ }
+
+ /**
+ * Validate that limit on number of requests is applied.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testRangingRequestPastLimit() {
+ ScanResult scanResult = new ScanResult();
+ List<ScanResult> scanResultList = new ArrayList<>();
+ for (int i = 0; i < RangingRequest.getMaxPeers() - 1; ++i) {
+ scanResultList.add(scanResult);
+ }
+
+ // create request
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ builder.addAp(scanResult);
+ builder.addAps(scanResultList);
+ builder.addAp(scanResult);
+ RangingRequest request = builder.build();
+
+ // verify request
+ request.enforceValidity();
+ }
+
+ /**
+ * Validate that RangingResults parcel works (produces same object on write/read).
+ */
+ @Test
+ public void testRangingResultsParcel() {
+ // Note: not validating parcel code of ScanResult (assumed to work)
+ int status = RangingResultCallback.STATUS_SUCCESS;
+ final byte[] mac = HexEncoding.decode("000102030405".toCharArray(), false);
+ int distanceCm = 105;
+ int distanceStdDevCm = 10;
+ int rssi = 5;
+ long timestamp = System.currentTimeMillis();
+
+ RangingResult result = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
+ timestamp);
+
+ Parcel parcelW = Parcel.obtain();
+ result.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ RangingResult rereadResult = RangingResult.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(result, rereadResult);
+ }
+}