Merge "Add pushing many batterystats events to statsd"
diff --git a/Android.mk b/Android.mk
index 6470e34..c87aec4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -425,7 +425,6 @@
core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl \
core/java/com/android/internal/widget/ILockSettings.aidl \
core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
- core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
keystore/java/android/security/IKeyChainAliasCallback.aidl \
keystore/java/android/security/IKeyChainService.aidl \
location/java/android/location/IBatchedLocationCallback.aidl \
@@ -579,6 +578,8 @@
LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings
+LOCAL_AIDL_INCLUDES += core/java/android/os/StatsLogEventWrapper.aidl
+
LOCAL_AIDL_INCLUDES += frameworks/base/lowpan/java
LOCAL_SRC_FILES += \
lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl \
diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk
index 200f92f..f08b402 100644
--- a/apct-tests/perftests/core/Android.mk
+++ b/apct-tests/perftests/core/Android.mk
@@ -4,11 +4,14 @@
LOCAL_MODULE_TAGS := tests
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ src/android/os/ISomeService.aidl
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
apct-perftests-utils \
+ guava \
legacy-android-test
LOCAL_PACKAGE_NAME := CorePerfTests
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index a709ee3..f27e8c8 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -5,6 +5,7 @@
<application>
<uses-library android:name="android.test.runner" />
<activity android:name="android.perftests.utils.StubActivity" />
+ <service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" />
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/apct-tests/perftests/core/src/android/os/ISomeService.aidl b/apct-tests/perftests/core/src/android/os/ISomeService.aidl
new file mode 100644
index 0000000..0658b69
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/ISomeService.aidl
@@ -0,0 +1,5 @@
+package android.os;
+
+interface ISomeService {
+ void readDisk(int times);
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/os/SomeService.java b/apct-tests/perftests/core/src/android/os/SomeService.java
new file mode 100644
index 0000000..bdfaa44
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/SomeService.java
@@ -0,0 +1,42 @@
+package android.os;
+
+import android.app.Service;
+import android.content.Intent;
+import java.io.File;
+import java.io.IOException;
+
+/** Service in separate process available for calling over binder. */
+public class SomeService extends Service {
+
+ private File mTempFile;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ try {
+ mTempFile = File.createTempFile("foo", "bar");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private final ISomeService.Stub mBinder =
+ new ISomeService.Stub() {
+ public void readDisk(int times) {
+ for (int i = 0; i < times; i++) {
+ mTempFile.exists();
+ }
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mTempFile.delete();
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/os/StrictModeTest.java b/apct-tests/perftests/core/src/android/os/StrictModeTest.java
new file mode 100644
index 0000000..d973c20
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/StrictModeTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.os;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import com.google.common.util.concurrent.SettableFuture;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class StrictModeTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeVmViolation() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
+ causeVmViolations(state);
+ }
+
+ @Test
+ public void timeVmViolationNoStrictMode() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ causeVmViolations(state);
+ }
+
+ private static void causeVmViolations(BenchmarkState state) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setDataAndType(Uri.parse("content://com.example/foobar"), "image/jpeg");
+ final Context context = InstrumentationRegistry.getTargetContext();
+ while (state.keepRunning()) {
+ context.startActivity(intent);
+ }
+ }
+
+ @Test
+ public void timeThreadViolation() throws IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ StrictMode.setThreadPolicy(
+ new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
+ causeThreadViolations(state);
+ }
+
+ @Test
+ public void timeThreadViolationNoStrictMode() throws IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ causeThreadViolations(state);
+ }
+
+ private static void causeThreadViolations(BenchmarkState state) throws IOException {
+ final File test = File.createTempFile("foo", "bar");
+ while (state.keepRunning()) {
+ test.exists();
+ }
+ test.delete();
+ }
+
+ @Test
+ public void timeCrossBinderThreadViolation() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ StrictMode.setThreadPolicy(
+ new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
+ causeCrossProcessThreadViolations(state);
+ }
+
+ @Test
+ public void timeCrossBinderThreadViolationNoStrictMode() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ causeCrossProcessThreadViolations(state);
+ }
+
+ private static void causeCrossProcessThreadViolations(BenchmarkState state)
+ throws ExecutionException, InterruptedException, RemoteException {
+ final Context context = InstrumentationRegistry.getTargetContext();
+
+ SettableFuture<IBinder> binder = SettableFuture.create();
+ ServiceConnection connection =
+ new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ binder.set(service);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ binder.set(null);
+ }
+ };
+ context.bindService(
+ new Intent(context, SomeService.class), connection, Context.BIND_AUTO_CREATE);
+ ISomeService someService = ISomeService.Stub.asInterface(binder.get());
+ while (state.keepRunning()) {
+ // Violate strictmode heavily.
+ someService.readDisk(10);
+ }
+ context.unbindService(connection);
+ }
+}
diff --git a/api/current.txt b/api/current.txt
index df8b7ef..8b3c740 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6451,6 +6451,8 @@
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
+ method public boolean setTime(android.content.ComponentName, long);
+ method public boolean setTimeZone(android.content.ComponentName, java.lang.String);
method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
diff --git a/api/system-current.txt b/api/system-current.txt
index 007316f..ee23b6d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6686,6 +6686,8 @@
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
+ method public boolean setTime(android.content.ComponentName, long);
+ method public boolean setTimeZone(android.content.ComponentName, java.lang.String);
method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
diff --git a/api/test-current.txt b/api/test-current.txt
index faccb6b..9a241ad 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6519,6 +6519,8 @@
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
+ method public boolean setTime(android.content.ComponentName, long);
+ method public boolean setTimeZone(android.content.ComponentName, java.lang.String);
method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 3c2f2d5..8946aed 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -37,6 +37,7 @@
src/matchers/matcher_util.cpp \
src/matchers/SimpleLogMatchingTracker.cpp \
src/metrics/CountAnomalyTracker.cpp \
+ src/metrics/MetricProducer.cpp \
src/metrics/CountMetricProducer.cpp \
src/metrics/DurationMetricProducer.cpp \
src/metrics/MetricsManager.cpp \
@@ -49,7 +50,8 @@
src/stats_util.cpp
statsd_common_c_includes := \
- $(LOCAL_PATH)/src
+ $(LOCAL_PATH)/src \
+ $(LOCAL_PATH)/../../libs/services/include
statsd_common_aidl_includes := \
$(LOCAL_PATH)/../../core/java
@@ -95,7 +97,7 @@
endif
LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
-LOCAL_AIDL_INCLUDES := $(statsd_common_c_includes)
+LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
LOCAL_C_INCLUDES += $(statsd_common_c_includes)
LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries)
@@ -117,7 +119,7 @@
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_MODULE_TAGS := tests
-LOCAL_AIDL_INCLUDES := $(statsd_common_c_includes)
+LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
LOCAL_C_INCLUDES += $(statsd_common_c_includes)
LOCAL_CFLAGS += \
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 87616d3..1faeee0 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -63,9 +63,9 @@
// ======================================================================
StatsService::StatsService(const sp<Looper>& handlerLooper)
- : mStatsPullerManager(),
- mAnomalyMonitor(new AnomalyMonitor(2)) // TODO: Put this comment somewhere better
+ : mAnomalyMonitor(new AnomalyMonitor(2)) // TODO: Put this comment somewhere better
{
+ mStatsPullerManager = new StatsPullerManager();
mUidMap = new UidMap();
mConfigManager = new ConfigManager();
mProcessor = new StatsLogProcessor(mUidMap);
@@ -193,6 +193,10 @@
if (!args[0].compare(String8("dump-report"))) {
return cmd_dump_report(out, err, args);
}
+
+ if (!args[0].compare(String8("pull-source")) && args.size() > 1) {
+ return cmd_print_pulled_metrics(out, args);
+ }
}
print_cmd_help(out);
@@ -210,6 +214,11 @@
fprintf(out, " Prints the UID, app name, version mapping.\n");
fprintf(out, "\n");
fprintf(out, "\n");
+ fprintf(out, "usage: adb shell cmds stats pull-source [int] \n");
+ fprintf(out, "\n");
+ fprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
fprintf(out, "usage: adb shell cmd stats config remove [UID] NAME\n");
fprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n");
fprintf(out, "\n");
@@ -353,6 +362,16 @@
return NO_ERROR;
}
+status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
+ int s = atoi(args[1].c_str());
+ auto stats = mStatsPullerManager->Pull(s);
+ for (const auto& it : stats) {
+ fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
+ }
+ fprintf(out, "Pull from %d: Received %zu elements\n", s, stats.size());
+ return NO_ERROR;
+}
+
Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
const vector<String16>& app) {
if (DEBUG) ALOGD("StatsService::informAllUidData was called");
@@ -414,10 +433,6 @@
if (DEBUG) ALOGD("StatsService::informPollAlarmFired succeeded");
// TODO: determine what services to poll and poll (or ask StatsCompanionService to poll) them.
- String16 output = mStatsPullerManager.pull(StatsPullerManager::KERNEL_WAKELOCKS);
- // TODO: do something useful with the output instead of writing a string to screen.
- ALOGD("%s", String8(output).string());
- ALOGD("%d", int(output.size()));
return Status::ok();
}
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 294aec8..449a2b8 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -121,6 +121,11 @@
status_t cmd_print_uid_map(FILE* out);
/**
+ * Print contents of a pulled metrics source.
+ */
+ status_t cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args);
+
+ /**
* Update a configuration.
*/
void set_config(int uid, const string& name, const StatsdConfig& config);
@@ -132,9 +137,8 @@
/**
* Fetches external metrics.
- * TODO: This should be an sp<>
*/
- StatsPullerManager mStatsPullerManager;
+ sp<StatsPullerManager> mStatsPullerManager;
/**
* Tracks the configurations that have been passed to statsd.
diff --git a/cmds/statsd/src/external/KernelWakelockPuller.cpp b/cmds/statsd/src/external/KernelWakelockPuller.cpp
index b9abee0..ee072f8 100644
--- a/cmds/statsd/src/external/KernelWakelockPuller.cpp
+++ b/cmds/statsd/src/external/KernelWakelockPuller.cpp
@@ -37,9 +37,9 @@
// The reading and parsing are implemented in Java. It is not difficult to port over. But for now
// let StatsCompanionService handle that and send the data back.
-String16 KernelWakelockPuller::pull() {
+vector<StatsLogEventWrapper> KernelWakelockPuller::pull() {
sp<IStatsCompanionService> statsCompanion = StatsService::getStatsCompanionService();
- String16 returned_value("");
+ vector<StatsLogEventWrapper> returned_value;
if (statsCompanion != NULL) {
Status status = statsCompanion->pullData(KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS,
&returned_value);
@@ -47,12 +47,10 @@
ALOGW("error pulling kernel wakelock");
}
ALOGD("KernelWakelockPuller::pull succeeded!");
- // TODO: remove this when we integrate into aggregation chain.
- ALOGD("%s", String8(returned_value).string());
return returned_value;
} else {
ALOGW("statsCompanion not found!");
- return String16();
+ return returned_value;
}
}
diff --git a/cmds/statsd/src/external/KernelWakelockPuller.h b/cmds/statsd/src/external/KernelWakelockPuller.h
index 1ec3376..c12806c 100644
--- a/cmds/statsd/src/external/KernelWakelockPuller.h
+++ b/cmds/statsd/src/external/KernelWakelockPuller.h
@@ -29,7 +29,7 @@
// a number of stats need to be pulled from StatsCompanionService
//
const static int PULL_CODE_KERNEL_WAKELOCKS;
- String16 pull() override;
+ vector<StatsLogEventWrapper> pull() override;
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 5e556b8..6655629 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -17,7 +17,12 @@
#ifndef STATSD_STATSPULLER_H
#define STATSD_STATSPULLER_H
+#include <android/os/StatsLogEventWrapper.h>
#include <utils/String16.h>
+#include <vector>
+
+using android::os::StatsLogEventWrapper;
+using std::vector;
namespace android {
namespace os {
@@ -26,8 +31,8 @@
class StatsPuller {
public:
virtual ~StatsPuller(){};
- // use string for now, until we figure out how to integrate into the aggregation path
- virtual String16 pull() = 0;
+
+ virtual vector<StatsLogEventWrapper> pull() = 0;
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 6e8d58bc..7f554d3 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -21,6 +21,11 @@
#include "KernelWakelockPuller.h"
#include "StatsService.h"
#include "external/StatsPullerManager.h"
+#include "logd/LogEvent.h"
+#include <cutils/log.h>
+#include <algorithm>
+
+#include <iostream>
using namespace android;
@@ -35,13 +40,27 @@
{static_cast<int>(KERNEL_WAKELOCKS), std::make_unique<KernelWakelockPuller>()});
}
-String16 StatsPullerManager::pull(int pullCode) {
+vector<std::shared_ptr<LogEvent>> StatsPullerManager::Pull(int pullCode) {
if (DEBUG) ALOGD("Initiating pulling %d", pullCode);
+
+ vector<std::shared_ptr<LogEvent>> ret;
if (mStatsPullers.find(pullCode) != mStatsPullers.end()) {
- return (mStatsPullers.find(pullCode)->second)->pull();
+ vector<StatsLogEventWrapper> outputs = (mStatsPullers.find(pullCode)->second)->pull();
+ for (const StatsLogEventWrapper& it : outputs) {
+ log_msg tmp;
+ tmp.entry_v1.len = it.bytes.size();
+ // Manually set the header size to 28 bytes to match the pushed log events.
+ tmp.entry.hdr_size = 28;
+ // And set the received bytes starting after the 28 bytes reserved for header.
+ std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + 28);
+ std::shared_ptr<LogEvent> evt = std::make_shared<LogEvent>(tmp);
+ ret.push_back(evt);
+ // ret.emplace_back(tmp);
+ }
+ return ret;
} else {
ALOGD("Unknown pull code %d", pullCode);
- return String16();
+ return ret; // Return early since we don't know what to pull.
}
}
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index f143424..e46aec1 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -20,6 +20,8 @@
#include <utils/String16.h>
#include <unordered_map>
#include "external/StatsPuller.h"
+#include "logd/LogEvent.h"
+#include "matchers/matcher_util.h"
namespace android {
namespace os {
@@ -27,7 +29,7 @@
const static int KERNEL_WAKELOCKS = 1;
-class StatsPullerManager {
+class StatsPullerManager : public virtual RefBase {
public:
// Enums of pulled data types (pullCodes)
// These values must be kept in sync with com/android/server/stats/StatsCompanionService.java.
@@ -35,7 +37,8 @@
const static int KERNEL_WAKELOCKS;
StatsPullerManager();
- String16 pull(const int pullCode);
+ // We return a vector of shared_ptr since LogEvent's copy constructor is not available.
+ vector<std::shared_ptr<LogEvent>> Pull(const int pullCode);
private:
std::unordered_map<int, std::unique_ptr<StatsPuller>> mStatsPullers;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 032b4b8..fb992c1 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -23,29 +23,30 @@
namespace statsd {
using std::ostringstream;
+using std::string;
-LogEvent::LogEvent(const log_msg& msg) {
- init(msg);
+// We need to keep a copy of the android_log_event_list owned by this instance so that the char*
+// for strings is not cleared before we can read them.
+LogEvent::LogEvent(log_msg msg) : mList(msg) {
+ init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &mList);
}
-LogEvent::LogEvent(int64_t timestampNs, android_log_event_list* reader) {
- init(timestampNs, reader);
+LogEvent::LogEvent(int tag) : mList(tag) {
}
LogEvent::~LogEvent() {
}
+void LogEvent::init() {
+ mList.convert_to_reader();
+ init(mTimestampNs, &mList);
+}
+
/**
* The elements of each log event are stored as a vector of android_log_list_elements.
* The goal is to do as little preprocessing as possible, because we read a tiny fraction
* of the elements that are written to the log.
*/
-void LogEvent::init(const log_msg& msg) {
-
- android_log_event_list list(const_cast<log_msg&>(msg));
- init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &list);
-}
-
void LogEvent::init(int64_t timestampNs, android_log_event_list* reader) {
mTimestampNs = timestampNs;
mTagId = reader->tag();
@@ -79,6 +80,10 @@
} while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
}
+android_log_event_list* LogEvent::GetAndroidLogEventList() {
+ return &mList;
+}
+
int64_t LogEvent::GetLong(size_t key, status_t* err) const {
if (key < 1 || (key - 1) >= mElements.size()) {
*err = BAD_INDEX;
@@ -109,7 +114,8 @@
*err = BAD_TYPE;
return NULL;
}
- return elem.data.string;
+ // Need to add the '/0' at the end by specifying the length of the string.
+ return string(elem.data.string, elem.len).c_str();
}
bool LogEvent::GetBool(size_t key, status_t* err) const {
@@ -189,7 +195,8 @@
} else if (elem.type == EVENT_TYPE_FLOAT) {
result << elem.data.float32;
} else if (elem.type == EVENT_TYPE_STRING) {
- result << elem.data.string;
+ // Need to add the '/0' at the end by specifying the length of the string.
+ result << string(elem.data.string, elem.len).c_str();
}
}
result << " }";
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 464afca..4102675 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -22,6 +22,7 @@
#include <log/log_event_list.h>
#include <log/log_read.h>
+#include <memory>
#include <string>
#include <vector>
@@ -40,12 +41,16 @@
/**
* Read a LogEvent from a log_msg.
*/
- explicit LogEvent(const log_msg& msg);
+ explicit LogEvent(log_msg msg);
/**
- * Read a LogEvent from an android_log_context.
+ * Constructs a LogEvent with the specified tag and creates an android_log_event_list in write
+ * mode. Obtain this list with the getter. Make sure to call init() before attempting to read
+ * any of the values. This constructor is useful for unit-testing since we can't pass in an
+ * android_log_event_list since there is no copy constructor or assignment operator available.
*/
- explicit LogEvent(int64_t timestampNs, android_log_event_list* reader);
+ explicit LogEvent(int tag);
+
~LogEvent();
/**
@@ -85,6 +90,19 @@
*/
KeyValuePair GetKeyValueProto(size_t key) const;
+ /**
+ * A pointer to the contained log_event_list.
+ *
+ * @return The android_log_event_list contained within.
+ */
+ android_log_event_list* GetAndroidLogEventList();
+
+ /**
+ * Used with the constructor where tag is passed in. Converts the log_event_list to read mode
+ * and prepares the list for reading.
+ */
+ void init();
+
private:
/**
* Don't copy, it's slower. If we really need this we can add it but let's try to
@@ -103,6 +121,8 @@
void init(int64_t timestampNs, android_log_event_list* reader);
vector<android_log_list_element> mElements;
+ // Need a copy of the android_log_event_list so the strings are not cleared.
+ android_log_event_list mList;
long mTimestampNs;
int mTagId;
};
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 1f07914..28cb503 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -124,52 +124,23 @@
mCondition = conditionMet;
}
-void CountMetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
+void CountMetricProducer::onMatchedLogEventInternal(
+ const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const map<string, HashableDimensionKey>& conditionKey, bool condition,
+ const LogEvent& event) {
uint64_t eventTimeNs = event.GetTimestampNs();
- // this is old event, maybe statsd restarted?
- if (eventTimeNs < mStartTimeNs) {
- return;
- }
flushCounterIfNeeded(eventTimeNs);
- if (mConditionSliced) {
- map<string, HashableDimensionKey> conditionKeys;
- for (const auto& link : mConditionLinks) {
- VLOG("Condition link key_in_main size %d", link.key_in_main_size());
- HashableDimensionKey conditionKey = getDimensionKeyForCondition(event, link);
- conditionKeys[link.condition()] = conditionKey;
- }
- if (mWizard->query(mConditionTrackerIndex, conditionKeys) != ConditionState::kTrue) {
- VLOG("metric %lld sliced condition not met", mMetric.metric_id());
- return;
- }
- } else {
- if (!mCondition) {
- VLOG("metric %lld condition not met", mMetric.metric_id());
- return;
- }
+ if (condition == false) {
+ return;
}
- HashableDimensionKey hashableKey;
-
- if (mDimension.size() > 0) {
- vector<KeyValuePair> key = getDimensionKey(event, mDimension);
- hashableKey = getHashableKey(key);
- // Add the HashableDimensionKey->vector<KeyValuePair> to the map, because StatsLogReport
- // expects vector<KeyValuePair>.
- if (mDimensionKeyMap.find(hashableKey) == mDimensionKeyMap.end()) {
- mDimensionKeyMap[hashableKey] = key;
- }
- } else {
- hashableKey = DEFAULT_DIMENSION_KEY;
- }
-
- auto it = mCurrentSlicedCounter.find(hashableKey);
+ auto it = mCurrentSlicedCounter.find(eventKey);
if (it == mCurrentSlicedCounter.end()) {
// create a counter for the new key
- mCurrentSlicedCounter[hashableKey] = 1;
+ mCurrentSlicedCounter[eventKey] = 1;
} else {
// increment the existing value
@@ -177,8 +148,8 @@
count++;
}
- VLOG("metric %lld %s->%d", mMetric.metric_id(), hashableKey.c_str(),
- mCurrentSlicedCounter[hashableKey]);
+ VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(),
+ mCurrentSlicedCounter[eventKey]);
}
// When a new matched event comes in, we check if event falls into the current
@@ -215,4 +186,4 @@
} // namespace statsd
} // namespace os
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index f0d6025..8bbdb01 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -41,8 +41,6 @@
virtual ~CountMetricProducer();
- void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) override;
-
void onConditionChanged(const bool conditionMet) override;
void finish() override;
@@ -54,6 +52,11 @@
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+protected:
+ void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const std::map<std::string, HashableDimensionKey>& conditionKey,
+ bool condition, const LogEvent& event) override;
+
private:
const CountMetric mMetric;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index aa597f4..38e55fd 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -137,11 +137,10 @@
return report;
};
-void DurationMetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
- if (event.GetTimestampNs() < mStartTimeNs) {
- return;
- }
-
+void DurationMetricProducer::onMatchedLogEventInternal(
+ const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const map<string, HashableDimensionKey>& conditionKeys, bool condition,
+ const LogEvent& event) {
flushDurationIfNeeded(event.GetTimestampNs());
if (matcherIndex == mStopAllIndex) {
@@ -149,49 +148,20 @@
return;
}
- HashableDimensionKey hashableKey;
- if (mDimension.size() > 0) {
- // hook up sliced counter with AnomalyMonitor.
- vector<KeyValuePair> key = getDimensionKey(event, mDimension);
- hashableKey = getHashableKey(key);
- // Add the HashableDimensionKey->DimensionKey to the map, because StatsLogReport expects
- // vector<KeyValuePair>.
- if (mDimensionKeyMap.find(hashableKey) == mDimensionKeyMap.end()) {
- mDimensionKeyMap[hashableKey] = key;
- }
- } else {
- hashableKey = DEFAULT_DIMENSION_KEY;
- }
-
- if (mCurrentSlicedDuration.find(hashableKey) == mCurrentSlicedDuration.end() &&
- mConditionSliced) {
+ if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end() && mConditionSliced) {
// add the durationInfo for the current bucket.
- auto& durationInfo = mCurrentSlicedDuration[hashableKey];
- auto& conditionKeys = durationInfo.conditionKeys;
- // get and cache the keys for query condition.
- for (const auto& link : mConditionLinks) {
- HashableDimensionKey conditionKey = getDimensionKeyForCondition(event, link);
- conditionKeys[link.condition()] = conditionKey;
- }
- }
-
- bool conditionMet;
- if (mConditionSliced) {
- const auto& conditionKeys = mCurrentSlicedDuration[hashableKey].conditionKeys;
- conditionMet =
- mWizard->query(mConditionTrackerIndex, conditionKeys) == ConditionState::kTrue;
- } else {
- conditionMet = mCondition;
+ auto& durationInfo = mCurrentSlicedDuration[eventKey];
+ durationInfo.conditionKeys = conditionKeys;
}
if (matcherIndex == mStartIndex) {
- VLOG("Metric %lld Key: %s Start, Condition %d", mMetric.metric_id(), hashableKey.c_str(),
- conditionMet);
- noteStart(hashableKey, conditionMet, event.GetTimestampNs());
+ VLOG("Metric %lld Key: %s Start, Condition %d", mMetric.metric_id(), eventKey.c_str(),
+ condition);
+ noteStart(eventKey, condition, event.GetTimestampNs());
} else if (matcherIndex == mStopIndex) {
- VLOG("Metric %lld Key: %s Stop, Condition %d", mMetric.metric_id(), hashableKey.c_str(),
- conditionMet);
- noteStop(hashableKey, event.GetTimestampNs());
+ VLOG("Metric %lld Key: %s Stop, Condition %d", mMetric.metric_id(), eventKey.c_str(),
+ condition);
+ noteStop(eventKey, event.GetTimestampNs());
}
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 44c3254..19e2437 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -61,8 +61,6 @@
virtual ~DurationMetricProducer();
- void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) override;
-
void onConditionChanged(const bool conditionMet) override;
void finish() override;
@@ -74,6 +72,11 @@
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+protected:
+ void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const std::map<std::string, HashableDimensionKey>& conditionKeys,
+ bool condition, const LogEvent& event) override;
+
private:
const DurationMetric mMetric;
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
new file mode 100644
index 0000000..3c8ce6e
--- /dev/null
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 "MetricProducer.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::map;
+
+void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
+ uint64_t eventTimeNs = event.GetTimestampNs();
+ // this is old event, maybe statsd restarted?
+ if (eventTimeNs < mStartTimeNs) {
+ return;
+ }
+
+ HashableDimensionKey eventKey;
+
+ if (mDimension.size() > 0) {
+ vector<KeyValuePair> key = getDimensionKey(event, mDimension);
+ eventKey = getHashableKey(key);
+ // Add the HashableDimensionKey->vector<KeyValuePair> to the map, because StatsLogReport
+ // expects vector<KeyValuePair>.
+ if (mDimensionKeyMap.find(eventKey) == mDimensionKeyMap.end()) {
+ mDimensionKeyMap[eventKey] = key;
+ }
+ } else {
+ eventKey = DEFAULT_DIMENSION_KEY;
+ }
+
+ bool condition;
+
+ map<string, HashableDimensionKey> conditionKeys;
+ if (mConditionSliced) {
+ for (const auto& link : mConditionLinks) {
+ HashableDimensionKey conditionKey = getDimensionKeyForCondition(event, link);
+ conditionKeys[link.condition()] = conditionKey;
+ }
+ if (mWizard->query(mConditionTrackerIndex, conditionKeys) != ConditionState::kTrue) {
+ condition = false;
+ } else {
+ condition = true;
+ }
+ } else {
+ condition = mCondition;
+ }
+
+ onMatchedLogEventInternal(matcherIndex, eventKey, conditionKeys, condition, event);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index afaab648..496b145 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -48,7 +48,7 @@
virtual ~MetricProducer(){};
// Consume the parsed stats log entry that already matched the "what" of the metric.
- virtual void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) = 0;
+ void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event);
virtual void onConditionChanged(const bool condition) = 0;
@@ -86,6 +86,26 @@
std::unordered_map<HashableDimensionKey, std::vector<KeyValuePair>> mDimensionKeyMap;
std::vector<EventConditionLink> mConditionLinks;
+
+ /*
+ * Individual metrics can implement their own business logic here. All pre-processing is done.
+ *
+ * [matcherIndex]: the index of the matcher which matched this event. This is interesting to
+ * DurationMetric, because it has start/stop/stop_all 3 matchers.
+ * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have
+ * dimensions, it will be DEFAULT_DIMENSION_KEY
+ * [conditionKey]: the keys of conditions which should be used to query the condition for this
+ * target event (from EventConditionLink). This is passed to individual metrics
+ * because DurationMetric needs it to be cached.
+ * [condition]: whether condition is met. If condition is sliced, this is the result coming from
+ * query with ConditionWizard; If condition is not sliced, this is the
+ * nonSlicedCondition.
+ * [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
+ */
+ virtual void onMatchedLogEventInternal(
+ const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+ const LogEvent& event) = 0;
};
} // namespace statsd
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 19403c0..fdfe8ef 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -42,12 +42,10 @@
auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
simpleMatcher->set_tag(TAG_ID);
- // Set up the event
- android_log_event_list list(TAG_ID);
+ LogEvent event(TAG_ID);
// Convert to a LogEvent
- list.convert_to_reader();
- LogEvent event(999, &list);
+ event.init();
// Test
EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
@@ -64,13 +62,13 @@
keyValue2->mutable_key_matcher()->set_key(FIELD_ID_2);
// Set up the event
- android_log_event_list list(TAG_ID);
- list << true;
- list << false;
+ LogEvent event(TAG_ID);
+ auto list = event.GetAndroidLogEventList();
+ *list << true;
+ *list << false;
// Convert to a LogEvent
- list.convert_to_reader();
- LogEvent event(999, &list);
+ event.init();
// Test
keyValue1->set_eq_bool(true);
@@ -100,12 +98,12 @@
keyValue->set_eq_string("some value");
// Set up the event
- android_log_event_list list(TAG_ID);
- list << "some value";
+ LogEvent event(TAG_ID);
+ auto list = event.GetAndroidLogEventList();
+ *list << "some value";
// Convert to a LogEvent
- list.convert_to_reader();
- LogEvent event(999, &list);
+ event.init();
// Test
EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
@@ -121,12 +119,11 @@
keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
// Set up the event
- android_log_event_list list(TAG_ID);
- list << 11;
+ LogEvent event(TAG_ID);
+ auto list = event.GetAndroidLogEventList();
+ *list << 11;
- // Convert to a LogEvent
- list.convert_to_reader();
- LogEvent event(999, &list);
+ event.init();
// Test
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3c53063..ab8edee 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6533,6 +6533,52 @@
}
/**
+ * Called by device owner to set the system wall clock time. This only takes effect if called
+ * when {@link android.provider.Settings.Global#AUTO_TIME} is 0, otherwise {@code false} will be
+ * returned.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with
+ * @param millis time in milliseconds since the Epoch
+ * @return {@code true} if set time succeeded, {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public boolean setTime(@NonNull ComponentName admin, long millis) {
+ throwIfParentInstance("setTime");
+ if (mService != null) {
+ try {
+ return mService.setTime(admin, millis);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Called by device owner to set the system's persistent default time zone. This only takes
+ * effect if called when {@link android.provider.Settings.Global#AUTO_TIME_ZONE} is 0, otherwise
+ * {@code false} will be returned.
+ *
+ * @see android.app.AlarmManager#setTimeZone(String)
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with
+ * @param timeZone one of the Olson ids from the list returned by
+ * {@link java.util.TimeZone#getAvailableIDs}
+ * @return {@code true} if set timezone succeeded, {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public boolean setTimeZone(@NonNull ComponentName admin, String timeZone) {
+ throwIfParentInstance("setTimeZone");
+ if (mService != null) {
+ try {
+ return mService.setTimeZone(admin, timeZone);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
* Called by profile or device owners to update {@link android.provider.Settings.Secure}
* settings. Validation that the value of the setting is in the correct form for the setting
* type should be performed by the caller.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 8865a05..e77c186 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -229,6 +229,9 @@
void setGlobalSetting(in ComponentName who, in String setting, in String value);
void setSecureSetting(in ComponentName who, in String setting, in String value);
+ boolean setTime(in ComponentName who, long millis);
+ boolean setTimeZone(in ComponentName who, String timeZone);
+
void setMasterVolumeMuted(in ComponentName admin, boolean on);
boolean isMasterVolumeMuted(in ComponentName admin);
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 969b19e..37bb6b0 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -20,17 +20,19 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
-import android.annotation.SystemService;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemService;
+import android.app.IServiceConnection;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.ServiceConnection;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
-import android.os.IBinder;
+import android.os.Handler;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -1051,43 +1053,23 @@
* The appWidgetId specified must already be bound to the calling AppWidgetHost via
* {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
*
- * @param packageName The package from which the binding is requested.
* @param appWidgetId The AppWidget instance for which to bind the RemoteViewsService.
* @param intent The intent of the service which will be providing the data to the
* RemoteViewsAdapter.
* @param connection The callback interface to be notified when a connection is made or lost.
+ * @param flags Flags used for binding to the service
+ *
+ * @see Context#getServiceDispatcher(ServiceConnection, Handler, int)
* @hide
*/
- public void bindRemoteViewsService(String packageName, int appWidgetId, Intent intent,
- IBinder connection) {
+ public boolean bindRemoteViewsService(Context context, int appWidgetId, Intent intent,
+ IServiceConnection connection, @Context.BindServiceFlags int flags) {
if (mService == null) {
- return;
+ return false;
}
try {
- mService.bindRemoteViewsService(packageName, appWidgetId, intent, connection);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Unbinds the RemoteViewsService for a given appWidgetId and intent.
- *
- * The appWidgetId specified muse already be bound to the calling AppWidgetHost via
- * {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
- *
- * @param packageName The package from which the binding is requested.
- * @param appWidgetId The AppWidget instance for which to bind the RemoteViewsService.
- * @param intent The intent of the service which will be providing the data to the
- * RemoteViewsAdapter.
- * @hide
- */
- public void unbindRemoteViewsService(String packageName, int appWidgetId, Intent intent) {
- if (mService == null) {
- return;
- }
- try {
- mService.unbindRemoteViewsService(packageName, appWidgetId, intent);
+ return mService.bindRemoteViewsService(context.getOpPackageName(), appWidgetId, intent,
+ context.getIApplicationThread(), context.getActivityToken(), connection, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 20fbf04..c165fb3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3604,7 +3604,6 @@
/**
* Use with {@link #getSystemService} to retrieve a
- * {@link android.text.ClipboardManager} for accessing and modifying
* {@link android.content.ClipboardManager} for accessing and modifying
* the contents of the global clipboard.
*
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 5995696..8682c01 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -447,8 +447,7 @@
/**
* Returns the max duration if it is being tracked.
- * Not all Timer subclasses track the max, total, current durations.
-
+ * Not all Timer subclasses track the max, total, and current durations.
*/
public long getMaxDurationMsLocked(long elapsedRealtimeMs) {
return -1;
@@ -456,14 +455,14 @@
/**
* Returns the current time the timer has been active, if it is being tracked.
- * Not all Timer subclasses track the max, total, current durations.
+ * Not all Timer subclasses track the max, total, and current durations.
*/
public long getCurrentDurationMsLocked(long elapsedRealtimeMs) {
return -1;
}
/**
- * Returns the current time the timer has been active, if it is being tracked.
+ * Returns the total time the timer has been active, if it is being tracked.
*
* Returns the total cumulative duration (i.e. sum of past durations) that this timer has
* been on since reset.
@@ -471,7 +470,7 @@
* depending on the Timer, getTotalTimeLocked may represent the total 'blamed' or 'pooled'
* time, rather than the actual time. By contrast, getTotalDurationMsLocked always gives
* the actual total time.
- * Not all Timer subclasses track the max, total, current durations.
+ * Not all Timer subclasses track the max, total, and current durations.
*/
public long getTotalDurationMsLocked(long elapsedRealtimeMs) {
return -1;
@@ -600,9 +599,17 @@
public abstract long getFullWifiLockTime(long elapsedRealtimeUs, int which);
public abstract long getWifiScanTime(long elapsedRealtimeUs, int which);
public abstract int getWifiScanCount(int which);
+ /**
+ * Returns the timer keeping track of wifi scans.
+ */
+ public abstract Timer getWifiScanTimer();
public abstract int getWifiScanBackgroundCount(int which);
public abstract long getWifiScanActualTime(long elapsedRealtimeUs);
public abstract long getWifiScanBackgroundTime(long elapsedRealtimeUs);
+ /**
+ * Returns the timer keeping track of background wifi scans.
+ */
+ public abstract Timer getWifiScanBackgroundTimer();
public abstract long getWifiBatchedScanTime(int csphBin, long elapsedRealtimeUs, int which);
public abstract int getWifiBatchedScanCount(int csphBin, int which);
public abstract long getWifiMulticastTime(long elapsedRealtimeUs, int which);
@@ -2824,6 +2831,10 @@
}
}
+ private static long roundUsToMs(long timeUs) {
+ return (timeUs + 500) / 1000;
+ }
+
private static long computeWakeLock(Timer timer, long elapsedRealtimeUs, int which) {
if (timer != null) {
// Convert from microseconds to milliseconds with rounding
@@ -3028,8 +3039,7 @@
Timer timer, long rawRealtime, int which) {
if (timer != null) {
// Convert from microseconds to milliseconds with rounding
- final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500)
- / 1000;
+ final long totalTime = roundUsToMs(timer.getTotalTimeLocked(rawRealtime, which));
final int count = timer.getCountLocked(which);
if (totalTime != 0 || count != 0) {
dumpLine(pw, uid, category, type, totalTime, count);
@@ -3052,12 +3062,26 @@
return;
}
// Convert from microseconds to milliseconds with rounding
- final long totalTimeMs = (timer.getTotalTimeLocked(rawRealtimeUs, which) + 500) / 1000;
+ final long timeMs = roundUsToMs(timer.getTotalTimeLocked(rawRealtimeUs, which));
final int count = timer.getCountLocked(which);
- if (totalTimeMs != 0 || count != 0) {
+ final long maxDurationMs = timer.getMaxDurationMsLocked(rawRealtimeUs / 1000);
+ final long curDurationMs = timer.getCurrentDurationMsLocked(rawRealtimeUs / 1000);
+ final long totalDurationMs = timer.getTotalDurationMsLocked(rawRealtimeUs / 1000);
+ if (timeMs != 0 || count != 0 || maxDurationMs != -1 || curDurationMs != -1
+ || totalDurationMs != -1) {
final long token = proto.start(fieldId);
- proto.write(TimerProto.DURATION_MS, totalTimeMs);
+ proto.write(TimerProto.DURATION_MS, timeMs);
proto.write(TimerProto.COUNT, count);
+ // These values will be -1 for timers that don't implement the functionality.
+ if (maxDurationMs != -1) {
+ proto.write(TimerProto.MAX_DURATION_MS, maxDurationMs);
+ }
+ if (curDurationMs != -1) {
+ proto.write(TimerProto.CURRENT_DURATION_MS, curDurationMs);
+ }
+ if (totalDurationMs != -1) {
+ proto.write(TimerProto.TOTAL_DURATION_MS, totalDurationMs);
+ }
proto.end(token);
}
}
@@ -3770,7 +3794,7 @@
linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_WINDOW),
rawRealtime, "w", which, linePrefix);
- // Only log if we had at lease one wakelock...
+ // Only log if we had at least one wakelock...
if (sb.length() > 0) {
String name = wakelocks.keyAt(iw);
if (name.indexOf(',') >= 0) {
@@ -6126,9 +6150,8 @@
return;
}
int count = steps.mNumStepDurations;
- long token;
for (int i = 0; i < count; ++i) {
- token = proto.start(fieldId);
+ long token = proto.start(fieldId);
proto.write(SystemProto.BatteryLevelStep.DURATION_MS, steps.getDurationAt(i));
proto.write(SystemProto.BatteryLevelStep.LEVEL, steps.getLevelAt(i));
@@ -6621,15 +6644,443 @@
}
if ((flags & (DUMP_HISTORY_ONLY | DUMP_DAILY_ONLY)) == 0) {
- // TODO: implement dumpProtoAppsLocked(proto, apps);
- dumpProtoSystemLocked(context, proto, (flags & DUMP_DEVICE_WIFI_ONLY) != 0);
+ final BatteryStatsHelper helper = new BatteryStatsHelper(context, false,
+ (flags & DUMP_DEVICE_WIFI_ONLY) != 0);
+ helper.create(this);
+ helper.refreshStats(STATS_SINCE_CHARGED, UserHandle.USER_ALL);
+
+ dumpProtoAppsLocked(proto, helper, apps);
+ dumpProtoSystemLocked(proto, helper);
}
proto.end(bToken);
proto.flush();
}
- private void dumpProtoSystemLocked(Context context, ProtoOutputStream proto, boolean wifiOnly) {
+ private void dumpProtoAppsLocked(ProtoOutputStream proto, BatteryStatsHelper helper,
+ List<ApplicationInfo> apps) {
+ final int which = STATS_SINCE_CHARGED;
+ final long rawUptimeUs = SystemClock.uptimeMillis() * 1000;
+ final long rawRealtimeMs = SystemClock.elapsedRealtime();
+ final long rawRealtimeUs = rawRealtimeMs * 1000;
+ final long batteryUptimeUs = getBatteryUptime(rawUptimeUs);
+
+ SparseArray<ArrayList<String>> aidToPackages = new SparseArray<>();
+ if (apps != null) {
+ for (int i = 0; i < apps.size(); ++i) {
+ ApplicationInfo ai = apps.get(i);
+ int aid = UserHandle.getAppId(ai.uid);
+ ArrayList<String> pkgs = aidToPackages.get(aid);
+ if (pkgs == null) {
+ pkgs = new ArrayList<String>();
+ aidToPackages.put(aid, pkgs);
+ }
+ pkgs.add(ai.packageName);
+ }
+ }
+
+ SparseArray<BatterySipper> uidToSipper = new SparseArray<>();
+ final List<BatterySipper> sippers = helper.getUsageList();
+ if (sippers != null) {
+ for (int i = 0; i < sippers.size(); ++i) {
+ final BatterySipper bs = sippers.get(i);
+ if (bs.drainType != BatterySipper.DrainType.APP) {
+ // Others are handled by dumpProtoSystemLocked()
+ continue;
+ }
+ uidToSipper.put(bs.uidObj.getUid(), bs);
+ }
+ }
+
+ SparseArray<? extends Uid> uidStats = getUidStats();
+ final int n = uidStats.size();
+ for (int iu = 0; iu < n; ++iu) {
+ final long uTkn = proto.start(BatteryStatsProto.UIDS);
+ final Uid u = uidStats.valueAt(iu);
+
+ final int uid = uidStats.keyAt(iu);
+ proto.write(UidProto.UID, uid);
+
+ // Print packages and apk stats (UID_DATA & APK_DATA)
+ ArrayList<String> pkgs = aidToPackages.get(UserHandle.getAppId(uid));
+ if (pkgs == null) {
+ pkgs = new ArrayList<String>();
+ }
+ final ArrayMap<String, ? extends BatteryStats.Uid.Pkg> packageStats =
+ u.getPackageStats();
+ for (int ipkg = packageStats.size() - 1; ipkg >= 0; --ipkg) {
+ String pkg = packageStats.keyAt(ipkg);
+ final ArrayMap<String, ? extends Uid.Pkg.Serv> serviceStats =
+ packageStats.valueAt(ipkg).getServiceStats();
+ if (serviceStats.size() == 0) {
+ // Due to the way ActivityManagerService logs wakeup alarms, some packages (for
+ // example, "android") may be included in the packageStats that aren't part of
+ // the UID. If they don't have any services, then they shouldn't be listed here.
+ // These packages won't be a part in the pkgs List.
+ continue;
+ }
+
+ final long pToken = proto.start(UidProto.PACKAGES);
+ proto.write(UidProto.Package.NAME, pkg);
+ // Remove from the packages list since we're logging it here.
+ pkgs.remove(pkg);
+
+ for (int isvc = serviceStats.size() - 1; isvc >= 0; --isvc) {
+ final BatteryStats.Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+ long sToken = proto.start(UidProto.Package.SERVICES);
+
+ proto.write(UidProto.Package.Service.NAME, serviceStats.keyAt(isvc));
+ proto.write(UidProto.Package.Service.START_DURATION_MS,
+ roundUsToMs(ss.getStartTime(batteryUptimeUs, which)));
+ proto.write(UidProto.Package.Service.START_COUNT, ss.getStarts(which));
+ proto.write(UidProto.Package.Service.LAUNCH_COUNT, ss.getLaunches(which));
+
+ proto.end(sToken);
+ }
+ proto.end(pToken);
+ }
+ // Print any remaining packages that weren't in the packageStats map. pkgs is pulled
+ // from PackageManager data. Packages are only included in packageStats if there was
+ // specific data tracked for them (services and wakeup alarms, etc.).
+ for (String p : pkgs) {
+ final long pToken = proto.start(UidProto.PACKAGES);
+ proto.write(UidProto.Package.NAME, p);
+ proto.end(pToken);
+ }
+
+ // Total wakelock data (AGGREGATED_WAKELOCK_DATA)
+ if (u.getAggregatedPartialWakelockTimer() != null) {
+ final Timer timer = u.getAggregatedPartialWakelockTimer();
+ // Times are since reset (regardless of 'which')
+ final long totTimeMs = timer.getTotalDurationMsLocked(rawRealtimeMs);
+ final Timer bgTimer = timer.getSubTimer();
+ final long bgTimeMs = bgTimer != null
+ ? bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+ final long awToken = proto.start(UidProto.AGGREGATED_WAKELOCK);
+ proto.write(UidProto.AggregatedWakelock.PARTIAL_DURATION_MS, totTimeMs);
+ proto.write(UidProto.AggregatedWakelock.BACKGROUND_PARTIAL_DURATION_MS, bgTimeMs);
+ proto.end(awToken);
+ }
+
+ // Audio (AUDIO_DATA)
+ dumpTimer(proto, UidProto.AUDIO, u.getAudioTurnedOnTimer(), rawRealtimeUs, which);
+
+ // Bluetooth Controller (BLUETOOTH_CONTROLLER_DATA)
+ dumpControllerActivityProto(proto, UidProto.BLUETOOTH_CONTROLLER,
+ u.getBluetoothControllerActivity(), which);
+
+ // BLE scans (BLUETOOTH_MISC_DATA) (uses totalDurationMsLocked and MaxDurationMsLocked)
+ final Timer bleTimer = u.getBluetoothScanTimer();
+ if (bleTimer != null) {
+ final long bmToken = proto.start(UidProto.BLUETOOTH_MISC);
+
+ dumpTimer(proto, UidProto.BluetoothMisc.APPORTIONED_BLE_SCAN, bleTimer,
+ rawRealtimeUs, which);
+ dumpTimer(proto, UidProto.BluetoothMisc.BACKGROUND_BLE_SCAN,
+ u.getBluetoothScanBackgroundTimer(), rawRealtimeUs, which);
+ // Unoptimized scan timer. Unpooled and since reset (regardless of 'which').
+ dumpTimer(proto, UidProto.BluetoothMisc.UNOPTIMIZED_BLE_SCAN,
+ u.getBluetoothUnoptimizedScanTimer(), rawRealtimeUs, which);
+ // Unoptimized bg scan timer. Unpooled and since reset (regardless of 'which').
+ dumpTimer(proto, UidProto.BluetoothMisc.BACKGROUND_UNOPTIMIZED_BLE_SCAN,
+ u.getBluetoothUnoptimizedScanBackgroundTimer(), rawRealtimeUs, which);
+ // Result counters
+ proto.write(UidProto.BluetoothMisc.BLE_SCAN_RESULT_COUNT,
+ u.getBluetoothScanResultCounter() != null
+ ? u.getBluetoothScanResultCounter().getCountLocked(which) : 0);
+ proto.write(UidProto.BluetoothMisc.BACKGROUND_BLE_SCAN_RESULT_COUNT,
+ u.getBluetoothScanResultBgCounter() != null
+ ? u.getBluetoothScanResultBgCounter().getCountLocked(which) : 0);
+
+ proto.end(bmToken);
+ }
+
+ // Camera (CAMERA_DATA)
+ dumpTimer(proto, UidProto.CAMERA, u.getCameraTurnedOnTimer(), rawRealtimeUs, which);
+
+ // CPU stats (CPU_DATA & CPU_TIMES_AT_FREQ_DATA)
+ final long cpuToken = proto.start(UidProto.CPU);
+ proto.write(UidProto.Cpu.USER_DURATION_MS, roundUsToMs(u.getUserCpuTimeUs(which)));
+ proto.write(UidProto.Cpu.SYSTEM_DURATION_MS, roundUsToMs(u.getSystemCpuTimeUs(which)));
+
+ final long[] cpuFreqs = getCpuFreqs();
+ if (cpuFreqs != null) {
+ final long[] cpuFreqTimeMs = u.getCpuFreqTimes(which);
+ // If total cpuFreqTimes is null, then we don't need to check for
+ // screenOffCpuFreqTimes.
+ if (cpuFreqTimeMs != null && cpuFreqTimeMs.length == cpuFreqs.length) {
+ long[] screenOffCpuFreqTimeMs = u.getScreenOffCpuFreqTimes(which);
+ if (screenOffCpuFreqTimeMs == null) {
+ screenOffCpuFreqTimeMs = new long[cpuFreqTimeMs.length];
+ }
+ for (int ic = 0; ic < cpuFreqTimeMs.length; ++ic) {
+ long cToken = proto.start(UidProto.Cpu.BY_FREQUENCY);
+ proto.write(UidProto.Cpu.ByFrequency.FREQUENCY_INDEX, ic + 1);
+ proto.write(UidProto.Cpu.ByFrequency.TOTAL_DURATION_MS,
+ cpuFreqTimeMs[ic]);
+ proto.write(UidProto.Cpu.ByFrequency.SCREEN_OFF_DURATION_MS,
+ screenOffCpuFreqTimeMs[ic]);
+ proto.end(cToken);
+ }
+ }
+ }
+ proto.end(cpuToken);
+
+ // Flashlight (FLASHLIGHT_DATA)
+ dumpTimer(proto, UidProto.FLASHLIGHT, u.getFlashlightTurnedOnTimer(),
+ rawRealtimeUs, which);
+
+ // Foreground activity (FOREGROUND_ACTIVITY_DATA)
+ dumpTimer(proto, UidProto.FOREGROUND_ACTIVITY, u.getForegroundActivityTimer(),
+ rawRealtimeUs, which);
+
+ // Foreground service (FOREGROUND_SERVICE_DATA)
+ dumpTimer(proto, UidProto.FOREGROUND_SERVICE, u.getForegroundServiceTimer(),
+ rawRealtimeUs, which);
+
+ // Job completion (JOB_COMPLETION_DATA)
+ final ArrayMap<String, SparseIntArray> completions = u.getJobCompletionStats();
+ final int[] reasons = new int[]{
+ JobParameters.REASON_CANCELED,
+ JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
+ JobParameters.REASON_PREEMPT,
+ JobParameters.REASON_TIMEOUT,
+ JobParameters.REASON_DEVICE_IDLE,
+ };
+ for (int ic = 0; ic < completions.size(); ++ic) {
+ SparseIntArray types = completions.valueAt(ic);
+ if (types != null) {
+ final long jcToken = proto.start(UidProto.JOB_COMPLETION);
+
+ proto.write(UidProto.JobCompletion.NAME, completions.keyAt(ic));
+
+ for (int r : reasons) {
+ long rToken = proto.start(UidProto.JobCompletion.REASON_COUNT);
+ proto.write(UidProto.JobCompletion.ReasonCount.NAME, r);
+ proto.write(UidProto.JobCompletion.ReasonCount.COUNT, types.get(r, 0));
+ proto.end(rToken);
+ }
+
+ proto.end(jcToken);
+ }
+ }
+
+ // Scheduled jobs (JOB_DATA)
+ final ArrayMap<String, ? extends Timer> jobs = u.getJobStats();
+ for (int ij = jobs.size() - 1; ij >= 0; --ij) {
+ final Timer timer = jobs.valueAt(ij);
+ final Timer bgTimer = timer.getSubTimer();
+ final long jToken = proto.start(UidProto.JOBS);
+
+ proto.write(UidProto.Job.NAME, jobs.keyAt(ij));
+ // Background uses totalDurationMsLocked, while total uses totalTimeLocked
+ dumpTimer(proto, UidProto.Job.TOTAL, timer, rawRealtimeUs, which);
+ dumpTimer(proto, UidProto.Job.BACKGROUND, bgTimer, rawRealtimeUs, which);
+
+ proto.end(jToken);
+ }
+
+ // Modem Controller (MODEM_CONTROLLER_DATA)
+ dumpControllerActivityProto(proto, UidProto.MODEM_CONTROLLER,
+ u.getModemControllerActivity(), which);
+
+ // Network stats (NETWORK_DATA)
+ final long nToken = proto.start(UidProto.NETWORK);
+ proto.write(UidProto.Network.MOBILE_BYTES_RX,
+ u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which));
+ proto.write(UidProto.Network.MOBILE_BYTES_TX,
+ u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which));
+ proto.write(UidProto.Network.WIFI_BYTES_RX,
+ u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
+ proto.write(UidProto.Network.WIFI_BYTES_TX,
+ u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
+ proto.write(UidProto.Network.BT_BYTES_RX,
+ u.getNetworkActivityBytes(NETWORK_BT_RX_DATA, which));
+ proto.write(UidProto.Network.BT_BYTES_TX,
+ u.getNetworkActivityBytes(NETWORK_BT_TX_DATA, which));
+ proto.write(UidProto.Network.MOBILE_PACKETS_RX,
+ u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which));
+ proto.write(UidProto.Network.MOBILE_PACKETS_TX,
+ u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which));
+ proto.write(UidProto.Network.WIFI_PACKETS_RX,
+ u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
+ proto.write(UidProto.Network.WIFI_PACKETS_TX,
+ u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
+ proto.write(UidProto.Network.MOBILE_ACTIVE_DURATION_MS,
+ roundUsToMs(u.getMobileRadioActiveTime(which)));
+ proto.write(UidProto.Network.MOBILE_ACTIVE_COUNT,
+ u.getMobileRadioActiveCount(which));
+ proto.write(UidProto.Network.MOBILE_WAKEUP_COUNT,
+ u.getMobileRadioApWakeupCount(which));
+ proto.write(UidProto.Network.WIFI_WAKEUP_COUNT,
+ u.getWifiRadioApWakeupCount(which));
+ proto.write(UidProto.Network.MOBILE_BYTES_BG_RX,
+ u.getNetworkActivityBytes(NETWORK_MOBILE_BG_RX_DATA, which));
+ proto.write(UidProto.Network.MOBILE_BYTES_BG_TX,
+ u.getNetworkActivityBytes(NETWORK_MOBILE_BG_TX_DATA, which));
+ proto.write(UidProto.Network.WIFI_BYTES_BG_RX,
+ u.getNetworkActivityBytes(NETWORK_WIFI_BG_RX_DATA, which));
+ proto.write(UidProto.Network.WIFI_BYTES_BG_TX,
+ u.getNetworkActivityBytes(NETWORK_WIFI_BG_TX_DATA, which));
+ proto.write(UidProto.Network.MOBILE_PACKETS_BG_RX,
+ u.getNetworkActivityPackets(NETWORK_MOBILE_BG_RX_DATA, which));
+ proto.write(UidProto.Network.MOBILE_PACKETS_BG_TX,
+ u.getNetworkActivityPackets(NETWORK_MOBILE_BG_TX_DATA, which));
+ proto.write(UidProto.Network.WIFI_PACKETS_BG_RX,
+ u.getNetworkActivityPackets(NETWORK_WIFI_BG_RX_DATA, which));
+ proto.write(UidProto.Network.WIFI_PACKETS_BG_TX,
+ u.getNetworkActivityPackets(NETWORK_WIFI_BG_TX_DATA, which));
+ proto.end(nToken);
+
+ // Power use item (POWER_USE_ITEM_DATA)
+ BatterySipper bs = uidToSipper.get(uid);
+ if (bs != null) {
+ final long bsToken = proto.start(UidProto.POWER_USE_ITEM);
+ proto.write(UidProto.PowerUseItem.COMPUTED_POWER_MAH, bs.totalPowerMah);
+ proto.write(UidProto.PowerUseItem.SHOULD_HIDE, bs.shouldHide);
+ proto.write(UidProto.PowerUseItem.SCREEN_POWER_MAH, bs.screenPowerMah);
+ proto.write(UidProto.PowerUseItem.PROPORTIONAL_SMEAR_MAH,
+ bs.proportionalSmearMah);
+ proto.end(bsToken);
+ }
+
+ // Processes (PROCESS_DATA)
+ final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats =
+ u.getProcessStats();
+ for (int ipr = processStats.size() - 1; ipr >= 0; --ipr) {
+ final Uid.Proc ps = processStats.valueAt(ipr);
+ final long prToken = proto.start(UidProto.PROCESS);
+
+ proto.write(UidProto.Process.NAME, processStats.keyAt(ipr));
+ proto.write(UidProto.Process.USER_DURATION_MS, ps.getUserTime(which));
+ proto.write(UidProto.Process.SYSTEM_DURATION_MS, ps.getSystemTime(which));
+ proto.write(UidProto.Process.FOREGROUND_DURATION_MS, ps.getForegroundTime(which));
+ proto.write(UidProto.Process.START_COUNT, ps.getStarts(which));
+ proto.write(UidProto.Process.ANR_COUNT, ps.getNumAnrs(which));
+ proto.write(UidProto.Process.CRASH_COUNT, ps.getNumCrashes(which));
+
+ proto.end(prToken);
+ }
+
+ // Sensors (SENSOR_DATA)
+ final SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+ for (int ise = 0; ise < sensors.size(); ++ise) {
+ final Uid.Sensor se = sensors.valueAt(ise);
+ final Timer timer = se.getSensorTime();
+ if (timer == null) {
+ continue;
+ }
+ final Timer bgTimer = se.getSensorBackgroundTime();
+ final int sensorNumber = sensors.keyAt(ise);
+ final long seToken = proto.start(UidProto.SENSORS);
+
+ proto.write(UidProto.Sensor.ID, sensorNumber);
+ // Background uses totalDurationMsLocked, while total uses totalTimeLocked
+ dumpTimer(proto, UidProto.Sensor.APPORTIONED, timer, rawRealtimeUs, which);
+ dumpTimer(proto, UidProto.Sensor.BACKGROUND, bgTimer, rawRealtimeUs, which);
+
+ proto.end(seToken);
+ }
+
+ // State times (STATE_TIME_DATA)
+ for (int ips = 0; ips < Uid.NUM_PROCESS_STATE; ++ips) {
+ long durMs = roundUsToMs(u.getProcessStateTime(ips, rawRealtimeUs, which));
+ if (durMs == 0) {
+ continue;
+ }
+ final long stToken = proto.start(UidProto.STATES);
+ proto.write(UidProto.StateTime.STATE, ips);
+ proto.write(UidProto.StateTime.DURATION_MS, durMs);
+ proto.end(stToken);
+ }
+
+ // Syncs (SYNC_DATA)
+ final ArrayMap<String, ? extends Timer> syncs = u.getSyncStats();
+ for (int isy = syncs.size() - 1; isy >= 0; --isy) {
+ final Timer timer = syncs.valueAt(isy);
+ final Timer bgTimer = timer.getSubTimer();
+ final long syToken = proto.start(UidProto.SYNCS);
+
+ proto.write(UidProto.Sync.NAME, syncs.keyAt(isy));
+ // Background uses totalDurationMsLocked, while total uses totalTimeLocked
+ dumpTimer(proto, UidProto.Sync.TOTAL, timer, rawRealtimeUs, which);
+ dumpTimer(proto, UidProto.Sync.BACKGROUND, bgTimer, rawRealtimeUs, which);
+
+ proto.end(syToken);
+ }
+
+ // User activity (USER_ACTIVITY_DATA)
+ if (u.hasUserActivity()) {
+ for (int i = 0; i < Uid.NUM_USER_ACTIVITY_TYPES; ++i) {
+ int val = u.getUserActivityCount(i, which);
+ if (val != 0) {
+ final long uaToken = proto.start(UidProto.USER_ACTIVITY);
+ proto.write(UidProto.UserActivity.NAME, i);
+ proto.write(UidProto.UserActivity.COUNT, val);
+ proto.end(uaToken);
+ }
+ }
+ }
+
+ // Vibrator (VIBRATOR_DATA)
+ dumpTimer(proto, UidProto.VIBRATOR, u.getVibratorOnTimer(), rawRealtimeUs, which);
+
+ // Video (VIDEO_DATA)
+ dumpTimer(proto, UidProto.VIDEO, u.getVideoTurnedOnTimer(), rawRealtimeUs, which);
+
+ // Wakelocks (WAKELOCK_DATA)
+ final ArrayMap<String, ? extends Uid.Wakelock> wakelocks = u.getWakelockStats();
+ for (int iw = wakelocks.size() - 1; iw >= 0; --iw) {
+ final Uid.Wakelock wl = wakelocks.valueAt(iw);
+ final long wToken = proto.start(UidProto.WAKELOCKS);
+ proto.write(UidProto.Wakelock.NAME, wakelocks.keyAt(iw));
+ dumpTimer(proto, UidProto.Wakelock.FULL, wl.getWakeTime(WAKE_TYPE_FULL),
+ rawRealtimeUs, which);
+ final Timer pTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+ if (pTimer != null) {
+ dumpTimer(proto, UidProto.Wakelock.PARTIAL, pTimer, rawRealtimeUs, which);
+ dumpTimer(proto, UidProto.Wakelock.BACKGROUND_PARTIAL, pTimer.getSubTimer(),
+ rawRealtimeUs, which);
+ }
+ dumpTimer(proto, UidProto.Wakelock.WINDOW, wl.getWakeTime(WAKE_TYPE_WINDOW),
+ rawRealtimeUs, which);
+ proto.end(wToken);
+ }
+
+ // Wakeup alarms (WAKEUP_ALARM_DATA)
+ for (int ipkg = packageStats.size() - 1; ipkg >= 0; --ipkg) {
+ final Uid.Pkg ps = packageStats.valueAt(ipkg);
+ final ArrayMap<String, ? extends Counter> alarms = ps.getWakeupAlarmStats();
+ for (int iwa = alarms.size() - 1; iwa >= 0; --iwa) {
+ final long waToken = proto.start(UidProto.WAKEUP_ALARM);
+ proto.write(UidProto.WakeupAlarm.NAME, alarms.keyAt(iwa));
+ proto.write(UidProto.WakeupAlarm.COUNT,
+ alarms.valueAt(iwa).getCountLocked(which));
+ proto.end(waToken);
+ }
+ }
+
+ // Wifi Controller (WIFI_CONTROLLER_DATA)
+ dumpControllerActivityProto(proto, UidProto.WIFI_CONTROLLER,
+ u.getWifiControllerActivity(), which);
+
+ // Wifi data (WIFI_DATA)
+ final long wToken = proto.start(UidProto.WIFI);
+ proto.write(UidProto.Wifi.FULL_WIFI_LOCK_DURATION_MS,
+ roundUsToMs(u.getFullWifiLockTime(rawRealtimeUs, which)));
+ dumpTimer(proto, UidProto.Wifi.APPORTIONED_SCAN, u.getWifiScanTimer(),
+ rawRealtimeUs, which);
+ proto.write(UidProto.Wifi.RUNNING_DURATION_MS,
+ roundUsToMs(u.getWifiRunningTime(rawRealtimeUs, which)));
+ dumpTimer(proto, UidProto.Wifi.BACKGROUND_SCAN, u.getWifiScanBackgroundTimer(),
+ rawRealtimeUs, which);
+ proto.end(wToken);
+
+ proto.end(uTkn);
+ }
+ }
+
+ private void dumpProtoSystemLocked(ProtoOutputStream proto, BatteryStatsHelper helper) {
final long sToken = proto.start(BatteryStatsProto.SYSTEM);
final long rawUptimeUs = SystemClock.uptimeMillis() * 1000;
final long rawRealtimeMs = SystemClock.elapsedRealtime();
@@ -6637,7 +7088,7 @@
final int which = STATS_SINCE_CHARGED;
// Battery data (BATTERY_DATA)
- long token = proto.start(SystemProto.BATTERY);
+ final long bToken = proto.start(SystemProto.BATTERY);
proto.write(SystemProto.Battery.START_CLOCK_TIME_MS, getStartClockTime());
proto.write(SystemProto.Battery.START_COUNT, getStartCount());
proto.write(SystemProto.Battery.TOTAL_REALTIME_MS,
@@ -6660,10 +7111,10 @@
getMinLearnedBatteryCapacity());
proto.write(SystemProto.Battery.MAX_LEARNED_BATTERY_CAPACITY_UAH,
getMaxLearnedBatteryCapacity());
- proto.end(token);
+ proto.end(bToken);
// Battery discharge (BATTERY_DISCHARGE_DATA)
- token = proto.start(SystemProto.BATTERY_DISCHARGE);
+ final long bdToken = proto.start(SystemProto.BATTERY_DISCHARGE);
proto.write(SystemProto.BatteryDischarge.LOWER_BOUND_SINCE_CHARGE,
getLowDischargeAmountSinceCharge());
proto.write(SystemProto.BatteryDischarge.UPPER_BOUND_SINCE_CHARGE,
@@ -6680,10 +7131,11 @@
getUahDischargeScreenOff(which) / 1000);
proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_SCREEN_DOZE,
getUahDischargeScreenDoze(which) / 1000);
- proto.end(token);
+ proto.end(bdToken);
// Time remaining
long timeRemainingUs = computeChargeTimeRemaining(rawRealtimeUs);
+ // These are part of a oneof, so we should only set one of them.
if (timeRemainingUs >= 0) {
// Charge time remaining (CHARGE_TIME_REMAIN_DATA)
proto.write(SystemProto.CHARGE_TIME_REMAINING_MS, timeRemainingUs / 1000);
@@ -6702,11 +7154,11 @@
// Phone data connection (DATA_CONNECTION_TIME_DATA and DATA_CONNECTION_COUNT_DATA)
for (int i = 0; i < NUM_DATA_CONNECTION_TYPES; ++i) {
- token = proto.start(SystemProto.DATA_CONNECTION);
+ final long pdcToken = proto.start(SystemProto.DATA_CONNECTION);
proto.write(SystemProto.DataConnection.NAME, i);
dumpTimer(proto, SystemProto.DataConnection.TOTAL, getPhoneDataConnectionTimer(i),
rawRealtimeUs, which);
- proto.end(token);
+ proto.end(pdcToken);
}
// Discharge step (DISCHARGE_STEP_DATA)
@@ -6729,7 +7181,7 @@
getModemControllerActivity(), which);
// Global network data (GLOBAL_NETWORK_DATA)
- token = proto.start(SystemProto.GLOBAL_NETWORK);
+ final long gnToken = proto.start(SystemProto.GLOBAL_NETWORK);
proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_RX,
getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which));
proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_TX,
@@ -6750,7 +7202,7 @@
getNetworkActivityBytes(NETWORK_BT_RX_DATA, which));
proto.write(SystemProto.GlobalNetwork.BT_BYTES_TX,
getNetworkActivityBytes(NETWORK_BT_TX_DATA, which));
- proto.end(token);
+ proto.end(gnToken);
// Wifi controller (GLOBAL_WIFI_CONTROLLER_DATA)
dumpControllerActivityProto(proto, SystemProto.GLOBAL_WIFI_CONTROLLER,
@@ -6758,21 +7210,21 @@
// Global wifi (GLOBAL_WIFI_DATA)
- token = proto.start(SystemProto.GLOBAL_WIFI);
+ final long gwToken = proto.start(SystemProto.GLOBAL_WIFI);
proto.write(SystemProto.GlobalWifi.ON_DURATION_MS,
getWifiOnTime(rawRealtimeUs, which) / 1000);
proto.write(SystemProto.GlobalWifi.RUNNING_DURATION_MS,
getGlobalWifiRunningTime(rawRealtimeUs, which) / 1000);
- proto.end(token);
+ proto.end(gwToken);
// Kernel wakelock (KERNEL_WAKELOCK_DATA)
final Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
- token = proto.start(SystemProto.KERNEL_WAKELOCK);
+ final long kwToken = proto.start(SystemProto.KERNEL_WAKELOCK);
proto.write(SystemProto.KernelWakelock.NAME, ent.getKey());
dumpTimer(proto, SystemProto.KernelWakelock.TOTAL, ent.getValue(),
rawRealtimeUs, which);
- proto.end(token);
+ proto.end(kwToken);
}
// Misc (MISC_DATA)
@@ -6802,7 +7254,7 @@
}
}
}
- token = proto.start(SystemProto.MISC);
+ final long mToken = proto.start(SystemProto.MISC);
proto.write(SystemProto.Misc.SCREEN_ON_DURATION_MS,
getScreenOnTime(rawRealtimeUs, which) / 1000);
proto.write(SystemProto.Misc.PHONE_ON_DURATION_MS,
@@ -6845,11 +7297,7 @@
getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which));
proto.write(SystemProto.Misc.LONGEST_LIGHT_DOZE_DURATION_MS,
getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT));
- proto.end(token);
-
- final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
- helper.create(this);
- helper.refreshStats(which, UserHandle.USER_ALL);
+ proto.end(mToken);
// Power use item (POWER_USE_ITEM_DATA)
final List<BatterySipper> sippers = helper.getUsageList();
@@ -6881,7 +7329,7 @@
n = SystemProto.PowerUseItem.FLASHLIGHT;
break;
case APP:
- // dumpProtoAppLocked will handle this.
+ // dumpProtoAppsLocked will handle this.
continue;
case USER:
n = SystemProto.PowerUseItem.USER;
@@ -6900,7 +7348,7 @@
n = SystemProto.PowerUseItem.MEMORY;
break;
}
- token = proto.start(SystemProto.POWER_USE_ITEM);
+ final long puiToken = proto.start(SystemProto.POWER_USE_ITEM);
proto.write(SystemProto.PowerUseItem.NAME, n);
proto.write(SystemProto.PowerUseItem.UID, uid);
proto.write(SystemProto.PowerUseItem.COMPUTED_POWER_MAH, bs.totalPowerMah);
@@ -6908,39 +7356,39 @@
proto.write(SystemProto.PowerUseItem.SCREEN_POWER_MAH, bs.screenPowerMah);
proto.write(SystemProto.PowerUseItem.PROPORTIONAL_SMEAR_MAH,
bs.proportionalSmearMah);
- proto.end(token);
+ proto.end(puiToken);
}
}
// Power use summary (POWER_USE_SUMMARY_DATA)
- token = proto.start(SystemProto.POWER_USE_SUMMARY);
+ final long pusToken = proto.start(SystemProto.POWER_USE_SUMMARY);
proto.write(SystemProto.PowerUseSummary.BATTERY_CAPACITY_MAH,
helper.getPowerProfile().getBatteryCapacity());
proto.write(SystemProto.PowerUseSummary.COMPUTED_POWER_MAH, helper.getComputedPower());
proto.write(SystemProto.PowerUseSummary.MIN_DRAINED_POWER_MAH, helper.getMinDrainedPower());
proto.write(SystemProto.PowerUseSummary.MAX_DRAINED_POWER_MAH, helper.getMaxDrainedPower());
- proto.end(token);
+ proto.end(pusToken);
// RPM stats (RESOURCE_POWER_MANAGER_DATA)
final Map<String, ? extends Timer> rpmStats = getRpmStats();
final Map<String, ? extends Timer> screenOffRpmStats = getScreenOffRpmStats();
for (Map.Entry<String, ? extends Timer> ent : rpmStats.entrySet()) {
- token = proto.start(SystemProto.RESOURCE_POWER_MANAGER);
+ final long rpmToken = proto.start(SystemProto.RESOURCE_POWER_MANAGER);
proto.write(SystemProto.ResourcePowerManager.NAME, ent.getKey());
dumpTimer(proto, SystemProto.ResourcePowerManager.TOTAL,
ent.getValue(), rawRealtimeUs, which);
dumpTimer(proto, SystemProto.ResourcePowerManager.SCREEN_OFF,
screenOffRpmStats.get(ent.getKey()), rawRealtimeUs, which);
- proto.end(token);
+ proto.end(rpmToken);
}
// Screen brightness (SCREEN_BRIGHTNESS_DATA)
for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; ++i) {
- token = proto.start(SystemProto.SCREEN_BRIGHTNESS);
+ final long sbToken = proto.start(SystemProto.SCREEN_BRIGHTNESS);
proto.write(SystemProto.ScreenBrightness.NAME, i);
dumpTimer(proto, SystemProto.ScreenBrightness.TOTAL, getScreenBrightnessTimer(i),
rawRealtimeUs, which);
- proto.end(token);
+ proto.end(sbToken);
}
// Signal scanning time (SIGNAL_SCANNING_TIME_DATA)
@@ -6949,47 +7397,47 @@
// Phone signal strength (SIGNAL_STRENGTH_TIME_DATA and SIGNAL_STRENGTH_COUNT_DATA)
for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; ++i) {
- token = proto.start(SystemProto.PHONE_SIGNAL_STRENGTH);
+ final long pssToken = proto.start(SystemProto.PHONE_SIGNAL_STRENGTH);
proto.write(SystemProto.PhoneSignalStrength.NAME, i);
dumpTimer(proto, SystemProto.PhoneSignalStrength.TOTAL, getPhoneSignalStrengthTimer(i),
rawRealtimeUs, which);
- proto.end(token);
+ proto.end(pssToken);
}
// Wakeup reasons (WAKEUP_REASON_DATA)
final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
- token = proto.start(SystemProto.WAKEUP_REASON);
+ final long wrToken = proto.start(SystemProto.WAKEUP_REASON);
proto.write(SystemProto.WakeupReason.NAME, ent.getKey());
dumpTimer(proto, SystemProto.WakeupReason.TOTAL, ent.getValue(), rawRealtimeUs, which);
- proto.end(token);
+ proto.end(wrToken);
}
// Wifi signal strength (WIFI_SIGNAL_STRENGTH_TIME_DATA and WIFI_SIGNAL_STRENGTH_COUNT_DATA)
for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; ++i) {
- token = proto.start(SystemProto.WIFI_SIGNAL_STRENGTH);
+ final long wssToken = proto.start(SystemProto.WIFI_SIGNAL_STRENGTH);
proto.write(SystemProto.WifiSignalStrength.NAME, i);
dumpTimer(proto, SystemProto.WifiSignalStrength.TOTAL, getWifiSignalStrengthTimer(i),
rawRealtimeUs, which);
- proto.end(token);
+ proto.end(wssToken);
}
// Wifi state (WIFI_STATE_TIME_DATA and WIFI_STATE_COUNT_DATA)
for (int i = 0; i < NUM_WIFI_STATES; ++i) {
- token = proto.start(SystemProto.WIFI_STATE);
+ final long wsToken = proto.start(SystemProto.WIFI_STATE);
proto.write(SystemProto.WifiState.NAME, i);
dumpTimer(proto, SystemProto.WifiState.TOTAL, getWifiStateTimer(i),
rawRealtimeUs, which);
- proto.end(token);
+ proto.end(wsToken);
}
// Wifi supplicant state (WIFI_SUPPL_STATE_TIME_DATA and WIFI_SUPPL_STATE_COUNT_DATA)
for (int i = 0; i < NUM_WIFI_SUPPL_STATES; ++i) {
- token = proto.start(SystemProto.WIFI_SUPPLICANT_STATE);
+ final long wssToken = proto.start(SystemProto.WIFI_SUPPLICANT_STATE);
proto.write(SystemProto.WifiSupplicantState.NAME, i);
dumpTimer(proto, SystemProto.WifiSupplicantState.TOTAL, getWifiSupplStateTimer(i),
rawRealtimeUs, which);
- proto.end(token);
+ proto.end(wssToken);
}
proto.end(sToken);
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index d8f9567..20f6c8e 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -16,6 +16,8 @@
package android.os;
+import android.os.StatsLogEventWrapper;
+
/**
* Binder interface to communicate with the Java-based statistics service helper.
* {@hide}
@@ -50,5 +52,5 @@
oneway void cancelPollingAlarms();
/** Pull the specified data. Results will be sent to statsd when complete. */
- String pullData(int pullCode);
+ StatsLogEventWrapper[] pullData(int pullCode);
}
diff --git a/core/java/android/service/autofill/SaveInfo.aidl b/core/java/android/os/StatsLogEventWrapper.aidl
similarity index 69%
rename from core/java/android/service/autofill/SaveInfo.aidl
rename to core/java/android/os/StatsLogEventWrapper.aidl
index 8cda608..766343e 100644
--- a/core/java/android/service/autofill/SaveInfo.aidl
+++ b/core/java/android/os/StatsLogEventWrapper.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2017, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -14,6 +14,7 @@
* limitations under the License.
*/
-package android.service.autofill;
+package android.os;
-parcelable SaveInfo;
+/** @hide */
+parcelable StatsLogEventWrapper cpp_header "android/os/StatsLogEventWrapper.h";
\ No newline at end of file
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
new file mode 100644
index 0000000..9491bec
--- /dev/null
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -0,0 +1,141 @@
+/*
+ * 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.os;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Wrapper class for sending data from Android OS to StatsD.
+ *
+ * @hide
+ */
+public final class StatsLogEventWrapper implements Parcelable {
+ private ByteArrayOutputStream mStorage = new ByteArrayOutputStream();
+
+ // Below are constants copied from log/log.h
+ private static final int EVENT_TYPE_INT = 0; /* int32_t */
+ private static final int EVENT_TYPE_LONG = 1; /* int64_t */
+ private static final int EVENT_TYPE_STRING = 2;
+ private static final int EVENT_TYPE_LIST = 3;
+ private static final int EVENT_TYPE_FLOAT = 4;
+
+ /**
+ * Creates a log_event that is binary-encoded as implemented in
+ * system/core/liblog/log_event_list.c; this allows us to use the same parsing logic in statsd
+ * for pushed and pulled data. The write* methods must be called in the same order as their
+ * field number. There is no checking that the correct number of write* methods is called.
+ * We also write an END_LIST character before beginning to write to parcel, but this END_LIST
+ * may be unnecessary.
+ *
+ * @param tag The integer representing the tag for this event.
+ * @param fields The number of fields specified in this event.
+ */
+ public StatsLogEventWrapper(int tag, int fields) {
+ // Write four bytes from tag, starting with least-significant bit.
+ write4Bytes(tag);
+ mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
+ mStorage.write(fields); // Indicate number of elements in this list.
+ }
+
+ /**
+ * Boilerplate for Parcel.
+ */
+ public static final Parcelable.Creator<StatsLogEventWrapper> CREATOR = new
+ Parcelable.Creator<StatsLogEventWrapper>() {
+ public StatsLogEventWrapper createFromParcel(Parcel in) {
+ return new StatsLogEventWrapper(in);
+ }
+
+ public StatsLogEventWrapper[] newArray(int size) {
+ return new StatsLogEventWrapper[size];
+ }
+ };
+
+ private void write4Bytes(int val) {
+ mStorage.write(val);
+ mStorage.write(val >>> 8);
+ mStorage.write(val >>> 16);
+ mStorage.write(val >>> 24);
+ }
+
+ private void write8Bytes(long val) {
+ write4Bytes((int) (val & 0xFFFFFFFF)); // keep the lowe 32-bits
+ write4Bytes((int) (val >>> 32)); // Write the high 32-bits.
+ }
+
+ /**
+ * Adds 32-bit integer to output.
+ */
+ public void writeInt(int val) {
+ mStorage.write(EVENT_TYPE_INT);
+ write4Bytes(val);
+ }
+
+ /**
+ * Adds 64-bit long to output.
+ */
+ public void writeLong(long val) {
+ mStorage.write(EVENT_TYPE_LONG);
+ write8Bytes(val);
+ }
+
+ /**
+ * Adds a 4-byte floating point value to output.
+ */
+ public void writeFloat(float val) {
+ int v = Float.floatToIntBits(val);
+ mStorage.write(EVENT_TYPE_FLOAT);
+ write4Bytes(v);
+ }
+
+ /**
+ * Adds a string to the output.
+ */
+ public void writeString(String val) {
+ mStorage.write(EVENT_TYPE_STRING);
+ write4Bytes(val.length());
+ byte[] bytes = val.getBytes(StandardCharsets.UTF_8);
+ mStorage.write(bytes, 0, bytes.length);
+ }
+
+ private StatsLogEventWrapper(Parcel in) {
+ readFromParcel(in);
+ }
+
+ /**
+ * Writes the stored fields to a byte array. Will first write a new-line character to denote
+ * END_LIST before writing contents to byte array.
+ */
+ public void writeToParcel(Parcel out, int flags) {
+ mStorage.write(10); // new-line character is same as END_LIST
+ out.writeByteArray(mStorage.toByteArray());
+ }
+
+ /**
+ * Not implemented.
+ */
+ public void readFromParcel(Parcel in) {
+ // Not needed since this java class is for sending to statsd only.
+ }
+
+ /**
+ * Boilerplate for Parcel.
+ */
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/service/autofill/Dataset.aidl b/core/java/android/service/autofill/Dataset.aidl
deleted file mode 100644
index 2342c5f..0000000
--- a/core/java/android/service/autofill/Dataset.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2016, 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.service.autofill;
-
-parcelable Dataset;
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index b2cdef2..331130e 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -150,8 +150,16 @@
public String toString() {
if (!sDebug) return super.toString();
- return new StringBuilder("Dataset " + mId + " [")
- .append("fieldIds=").append(mFieldIds)
+ final StringBuilder builder = new StringBuilder("Dataset[id=");
+ if (mId == null) {
+ builder.append("null");
+ } else {
+ // Cannot disclose id because it could contain PII.
+ builder.append(mId.length()).append("_chars");
+ }
+
+ return builder
+ .append(", fieldIds=").append(mFieldIds)
.append(", fieldValues=").append(mFieldValues)
.append(", fieldPresentations=")
.append(mFieldPresentations == null ? 0 : mFieldPresentations.size())
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index d6c0dbf..b1857b3 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -129,6 +129,11 @@
}
@Override
+ public String toString() {
+ return mEvents == null ? "no events" : mEvents.toString();
+ }
+
+ @Override
public int describeContents() {
return 0;
}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index d91cebb..d2033fa 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -71,7 +71,7 @@
private FillResponse(@NonNull Builder builder) {
mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
mSaveInfo = builder.mSaveInfo;
- mClientState = builder.mCLientState;
+ mClientState = builder.mClientState;
mPresentation = builder.mPresentation;
mAuthentication = builder.mAuthentication;
mAuthenticationIds = builder.mAuthenticationIds;
@@ -145,7 +145,7 @@
public static final class Builder {
private ArrayList<Dataset> mDatasets;
private SaveInfo mSaveInfo;
- private Bundle mCLientState;
+ private Bundle mClientState;
private RemoteViews mPresentation;
private IntentSender mAuthentication;
private AutofillId[] mAuthenticationIds;
@@ -288,7 +288,7 @@
*/
public Builder setClientState(@Nullable Bundle clientState) {
throwIfDestroyed();
- mCLientState = clientState;
+ mClientState = clientState;
return this;
}
diff --git a/core/java/android/view/autofill/AutoFillType.aidl b/core/java/android/view/autofill/AutoFillType.aidl
deleted file mode 100644
index 4606b48..0000000
--- a/core/java/android/view/autofill/AutoFillType.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.autofill;
-
-/*
- * TODO(b/35956626): remove once clients use getAutoFilltype()
- */
-parcelable AutoFillType;
\ No newline at end of file
diff --git a/core/java/android/view/autofill/AutoFillValue.aidl b/core/java/android/view/autofill/AutoFillValue.aidl
deleted file mode 100644
index 05b7562..0000000
--- a/core/java/android/view/autofill/AutoFillValue.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.autofill;
-
-// @deprecated TODO(b/35956626): remove once clients use AutofillValue
-parcelable AutoFillValue;
\ No newline at end of file
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index af09592..384f4f8 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -476,17 +476,6 @@
stopTextActionModeWithPreservingSelection();
}
- void invalidateMagnifier() {
- final DisplayMetrics dm = mTextView.getResources().getDisplayMetrics();
- invalidateMagnifier(0, 0, dm.widthPixels, dm.heightPixels);
- }
-
- void invalidateMagnifier(final float l, final float t, final float r, final float b) {
- if (mMagnifier != null) {
- mTextView.post(() -> mMagnifier.invalidate(new RectF(l, t, r, b)));
- }
- }
-
private void discardTextDisplayLists() {
if (mTextRenderNodes != null) {
for (int i = 0; i < mTextRenderNodes.length; i++) {
@@ -4550,17 +4539,15 @@
final Layout layout = mTextView.getLayout();
final int lineNumber = layout.getLineForOffset(offset);
// Horizontally snap to character offset.
- final float xPosInView = getHorizontal(mTextView.getLayout(), offset);
+ final float xPosInView = getHorizontal(mTextView.getLayout(), offset)
+ + mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
// Vertically snap to middle of current line.
final float yPosInView = (mTextView.getLayout().getLineTop(lineNumber)
- + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f;
- final int[] coordinatesOnScreen = new int[2];
- mTextView.getLocationOnScreen(coordinatesOnScreen);
- final float centerXOnScreen = mTextView.convertViewToScreenCoord(xPosInView, true);
- final float centerYOnScreen = mTextView.convertViewToScreenCoord(yPosInView, false);
+ + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f
+ + mTextView.getTotalPaddingTop() - mTextView.getScrollY();
suspendBlink();
- mMagnifier.show(centerXOnScreen, centerYOnScreen, MAGNIFIER_ZOOM);
+ mMagnifier.show(xPosInView, yPosInView, MAGNIFIER_ZOOM);
}
protected final void dismissMagnifier() {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 51277e4..e5ae0ca 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -16,11 +16,14 @@
package android.widget;
-import android.Manifest;
+import android.annotation.WorkerThread;
+import android.app.IServiceConnection;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.os.Handler;
import android.os.HandlerThread;
@@ -29,7 +32,6 @@
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -39,7 +41,6 @@
import android.view.ViewGroup;
import android.widget.RemoteViews.OnClickHandler;
-import com.android.internal.widget.IRemoteViewsAdapterConnection;
import com.android.internal.widget.IRemoteViewsFactory;
import java.lang.ref.WeakReference;
@@ -49,52 +50,33 @@
import java.util.concurrent.Executor;
/**
- * An adapter to a RemoteViewsService which fetches and caches RemoteViews
- * to be later inflated as child views.
+ * An adapter to a RemoteViewsService which fetches and caches RemoteViews to be later inflated as
+ * child views.
+ *
+ * The adapter runs in the host process, typically a Launcher app.
+ *
+ * It makes a service connection to the {@link RemoteViewsService} running in the
+ * AppWidgetsProvider's process. This connection is made on a background thread (and proxied via
+ * the platform to get the bind permissions) and all interaction with the service is done on the
+ * background thread.
+ *
+ * On first bind, the adapter will load can cache the RemoteViews locally. Afterwards the
+ * connection is only made when new RemoteViews are required.
+ * @hide
*/
-/** @hide */
public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback {
- private static final String MULTI_USER_PERM = Manifest.permission.INTERACT_ACROSS_USERS_FULL;
private static final String TAG = "RemoteViewsAdapter";
// The max number of items in the cache
- private static final int sDefaultCacheSize = 40;
+ private static final int DEFAULT_CACHE_SIZE = 40;
// The delay (in millis) to wait until attempting to unbind from a service after a request.
// This ensures that we don't stay continually bound to the service and that it can be destroyed
// if we need the memory elsewhere in the system.
- private static final int sUnbindServiceDelay = 5000;
+ private static final int UNBIND_SERVICE_DELAY = 5000;
// Default height for the default loading view, in case we cannot get inflate the first view
- private static final int sDefaultLoadingViewHeight = 50;
-
- // Type defs for controlling different messages across the main and worker message queues
- private static final int sDefaultMessageType = 0;
- private static final int sUnbindServiceMessageType = 1;
-
- private final Context mContext;
- private final Intent mIntent;
- private final int mAppWidgetId;
- private final Executor mAsyncViewLoadExecutor;
-
- private RemoteViewsAdapterServiceConnection mServiceConnection;
- private WeakReference<RemoteAdapterConnectionCallback> mCallback;
- private OnClickHandler mRemoteViewsOnClickHandler;
- private final FixedSizeRemoteViewsCache mCache;
- private int mVisibleWindowLowerBound;
- private int mVisibleWindowUpperBound;
-
- // A flag to determine whether we should notify data set changed after we connect
- private boolean mNotifyDataSetChangedAfterOnServiceConnected = false;
-
- // The set of requested views that are to be notified when the associated RemoteViews are
- // loaded.
- private RemoteViewsFrameLayoutRefSet mRequestedViews;
-
- private HandlerThread mWorkerThread;
- // items may be interrupted within the normally processed queues
- private Handler mWorkerQueue;
- private Handler mMainQueue;
+ private static final int DEFAULT_LOADING_VIEW_HEIGHT = 50;
// We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data
// structures;
@@ -111,6 +93,26 @@
// duration, the cache is dropped.
private static final int REMOTE_VIEWS_CACHE_DURATION = 5000;
+ private final Context mContext;
+ private final Intent mIntent;
+ private final int mAppWidgetId;
+ private final Executor mAsyncViewLoadExecutor;
+
+ private OnClickHandler mRemoteViewsOnClickHandler;
+ private final FixedSizeRemoteViewsCache mCache;
+ private int mVisibleWindowLowerBound;
+ private int mVisibleWindowUpperBound;
+
+ // The set of requested views that are to be notified when the associated RemoteViews are
+ // loaded.
+ private RemoteViewsFrameLayoutRefSet mRequestedViews;
+
+ private final HandlerThread mWorkerThread;
+ // items may be interrupted within the normally processed queues
+ private final Handler mMainHandler;
+ private final RemoteServiceHandler mServiceHandler;
+ private final RemoteAdapterConnectionCallback mCallback;
+
// Used to indicate to the AdapterView that it can use this Adapter immediately after
// construction (happens when we have a cached FixedSizeRemoteViewsCache).
private boolean mDataReady = false;
@@ -158,154 +160,192 @@
}
}
+ static final int MSG_REQUEST_BIND = 1;
+ static final int MSG_NOTIFY_DATA_SET_CHANGED = 2;
+ static final int MSG_LOAD_NEXT_ITEM = 3;
+ static final int MSG_UNBIND_SERVICE = 4;
+
+ private static final int MSG_MAIN_HANDLER_COMMIT_METADATA = 1;
+ private static final int MSG_MAIN_HANDLER_SUPER_NOTIFY_DATA_SET_CHANGED = 2;
+ private static final int MSG_MAIN_HANDLER_REMOTE_ADAPTER_CONNECTED = 3;
+ private static final int MSG_MAIN_HANDLER_REMOTE_ADAPTER_DISCONNECTED = 4;
+ private static final int MSG_MAIN_HANDLER_REMOTE_VIEWS_LOADED = 5;
+
/**
- * The service connection that gets populated when the RemoteViewsService is
- * bound. This must be a static inner class to ensure that no references to the outer
- * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being
- * garbage collected, and would cause us to leak activities due to the caching mechanism for
- * FrameLayouts in the adapter).
+ * Handler for various interactions with the {@link RemoteViewsService}.
*/
- private static class RemoteViewsAdapterServiceConnection extends
- IRemoteViewsAdapterConnection.Stub {
- private boolean mIsConnected;
- private boolean mIsConnecting;
- private WeakReference<RemoteViewsAdapter> mAdapter;
+ private static class RemoteServiceHandler extends Handler implements ServiceConnection {
+
+ private final WeakReference<RemoteViewsAdapter> mAdapter;
+ private final Context mContext;
+
private IRemoteViewsFactory mRemoteViewsFactory;
- public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) {
- mAdapter = new WeakReference<RemoteViewsAdapter>(adapter);
+ // The last call to notifyDataSetChanged didn't succeed, try again on next service bind.
+ private boolean mNotifyDataSetChangedPending = false;
+ private boolean mBindRequested = false;
+
+ RemoteServiceHandler(Looper workerLooper, RemoteViewsAdapter adapter, Context context) {
+ super(workerLooper);
+ mAdapter = new WeakReference<>(adapter);
+ mContext = context;
}
- public synchronized void bind(Context context, int appWidgetId, Intent intent) {
- if (!mIsConnecting) {
- try {
- RemoteViewsAdapter adapter;
- final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
- if ((adapter = mAdapter.get()) != null) {
- mgr.bindRemoteViewsService(context.getOpPackageName(), appWidgetId,
- intent, asBinder());
- } else {
- Slog.w(TAG, "bind: adapter was null");
- }
- mIsConnecting = true;
- } catch (Exception e) {
- Log.e("RVAServiceConnection", "bind(): " + e.getMessage());
- mIsConnecting = false;
- mIsConnected = false;
- }
- }
- }
-
- public synchronized void unbind(Context context, int appWidgetId, Intent intent) {
- try {
- RemoteViewsAdapter adapter;
- final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
- if ((adapter = mAdapter.get()) != null) {
- mgr.unbindRemoteViewsService(context.getOpPackageName(), appWidgetId, intent);
- } else {
- Slog.w(TAG, "unbind: adapter was null");
- }
- mIsConnecting = false;
- } catch (Exception e) {
- Log.e("RVAServiceConnection", "unbind(): " + e.getMessage());
- mIsConnecting = false;
- mIsConnected = false;
- }
- }
-
- public synchronized void onServiceConnected(IBinder service) {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ // This is called on the same thread.
mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
+ enqueueDeferredUnbindServiceMessage();
- // Remove any deferred unbind messages
- final RemoteViewsAdapter adapter = mAdapter.get();
- if (adapter == null) return;
+ RemoteViewsAdapter adapter = mAdapter.get();
+ if (adapter == null) {
+ return;
+ }
- // Queue up work that we need to do for the callback to run
- adapter.mWorkerQueue.post(new Runnable() {
- @Override
- public void run() {
- if (adapter.mNotifyDataSetChangedAfterOnServiceConnected) {
- // Handle queued notifyDataSetChanged() if necessary
- adapter.onNotifyDataSetChanged();
- } else {
- IRemoteViewsFactory factory =
- adapter.mServiceConnection.getRemoteViewsFactory();
- try {
- if (!factory.isCreated()) {
- // We only call onDataSetChanged() if this is the factory was just
- // create in response to this bind
- factory.onDataSetChanged();
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error notifying factory of data set changed in " +
- "onServiceConnected(): " + e.getMessage());
-
- // Return early to prevent anything further from being notified
- // (effectively nothing has changed)
- return;
- } catch (RuntimeException e) {
- Log.e(TAG, "Error notifying factory of data set changed in " +
- "onServiceConnected(): " + e.getMessage());
- }
-
- // Request meta data so that we have up to date data when calling back to
- // the remote adapter callback
- adapter.updateTemporaryMetaData();
-
- // Notify the host that we've connected
- adapter.mMainQueue.post(new Runnable() {
- @Override
- public void run() {
- synchronized (adapter.mCache) {
- adapter.mCache.commitTemporaryMetaData();
- }
-
- final RemoteAdapterConnectionCallback callback =
- adapter.mCallback.get();
- if (callback != null) {
- callback.onRemoteAdapterConnected();
- }
- }
- });
- }
-
- // Enqueue unbind message
- adapter.enqueueDeferredUnbindServiceMessage();
- mIsConnected = true;
- mIsConnecting = false;
+ if (mNotifyDataSetChangedPending) {
+ mNotifyDataSetChangedPending = false;
+ Message msg = Message.obtain(this, MSG_NOTIFY_DATA_SET_CHANGED);
+ handleMessage(msg);
+ msg.recycle();
+ } else {
+ if (!sendNotifyDataSetChange(false)) {
+ return;
}
- });
+
+ // Request meta data so that we have up to date data when calling back to
+ // the remote adapter callback
+ adapter.updateTemporaryMetaData(mRemoteViewsFactory);
+ adapter.mMainHandler.sendEmptyMessage(MSG_MAIN_HANDLER_COMMIT_METADATA);
+ adapter.mMainHandler.sendEmptyMessage(MSG_MAIN_HANDLER_REMOTE_ADAPTER_CONNECTED);
+ }
}
- public synchronized void onServiceDisconnected() {
- mIsConnected = false;
- mIsConnecting = false;
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
mRemoteViewsFactory = null;
+ RemoteViewsAdapter adapter = mAdapter.get();
+ if (adapter != null) {
+ adapter.mMainHandler.sendEmptyMessage(MSG_MAIN_HANDLER_REMOTE_ADAPTER_DISCONNECTED);
+ }
+ }
- // Clear the main/worker queues
- final RemoteViewsAdapter adapter = mAdapter.get();
- if (adapter == null) return;
+ @Override
+ public void handleMessage(Message msg) {
+ RemoteViewsAdapter adapter = mAdapter.get();
- adapter.mMainQueue.post(new Runnable() {
- @Override
- public void run() {
- // Dequeue any unbind messages
- adapter.mMainQueue.removeMessages(sUnbindServiceMessageType);
-
- final RemoteAdapterConnectionCallback callback = adapter.mCallback.get();
- if (callback != null) {
- callback.onRemoteAdapterDisconnected();
+ switch (msg.what) {
+ case MSG_REQUEST_BIND: {
+ if (adapter == null || mRemoteViewsFactory != null) {
+ enqueueDeferredUnbindServiceMessage();
}
+ if (mBindRequested) {
+ return;
+ }
+ int flags = Context.BIND_AUTO_CREATE
+ | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE;
+ final IServiceConnection sd = mContext.getServiceDispatcher(this, this, flags);
+ Intent intent = (Intent) msg.obj;
+ int appWidgetId = msg.arg1;
+ mBindRequested = AppWidgetManager.getInstance(mContext)
+ .bindRemoteViewsService(mContext, appWidgetId, intent, sd, flags);
+ return;
}
- });
+ case MSG_NOTIFY_DATA_SET_CHANGED: {
+ enqueueDeferredUnbindServiceMessage();
+ if (adapter == null) {
+ return;
+ }
+ if (mRemoteViewsFactory == null) {
+ mNotifyDataSetChangedPending = true;
+ adapter.requestBindService();
+ return;
+ }
+ if (!sendNotifyDataSetChange(true)) {
+ return;
+ }
+
+ // Flush the cache so that we can reload new items from the service
+ synchronized (adapter.mCache) {
+ adapter.mCache.reset();
+ }
+
+ // Re-request the new metadata (only after the notification to the factory)
+ adapter.updateTemporaryMetaData(mRemoteViewsFactory);
+ int newCount;
+ int[] visibleWindow;
+ synchronized (adapter.mCache.getTemporaryMetaData()) {
+ newCount = adapter.mCache.getTemporaryMetaData().count;
+ visibleWindow = adapter.getVisibleWindow(newCount);
+ }
+
+ // Pre-load (our best guess of) the views which are currently visible in the
+ // AdapterView. This mitigates flashing and flickering of loading views when a
+ // widget notifies that its data has changed.
+ for (int position : visibleWindow) {
+ // Because temporary meta data is only ever modified from this thread
+ // (ie. mWorkerThread), it is safe to assume that count is a valid
+ // representation.
+ if (position < newCount) {
+ adapter.updateRemoteViews(mRemoteViewsFactory, position, false);
+ }
+ }
+
+ // Propagate the notification back to the base adapter
+ adapter.mMainHandler.sendEmptyMessage(MSG_MAIN_HANDLER_COMMIT_METADATA);
+ adapter.mMainHandler.sendEmptyMessage(
+ MSG_MAIN_HANDLER_SUPER_NOTIFY_DATA_SET_CHANGED);
+ return;
+ }
+
+ case MSG_LOAD_NEXT_ITEM: {
+ if (adapter == null || mRemoteViewsFactory == null) {
+ return;
+ }
+ removeMessages(MSG_UNBIND_SERVICE);
+ // Get the next index to load
+ final int position = adapter.mCache.getNextIndexToLoad();
+ if (position > -1) {
+ // Load the item, and notify any existing RemoteViewsFrameLayouts
+ adapter.updateRemoteViews(mRemoteViewsFactory, position, true);
+
+ // Queue up for the next one to load
+ sendEmptyMessage(MSG_LOAD_NEXT_ITEM);
+ } else {
+ // No more items to load, so queue unbind
+ enqueueDeferredUnbindServiceMessage();
+ }
+ return;
+ }
+ case MSG_UNBIND_SERVICE: {
+ unbindNow();
+ return;
+ }
+ }
}
- public synchronized IRemoteViewsFactory getRemoteViewsFactory() {
- return mRemoteViewsFactory;
+ protected void unbindNow() {
+ if (mBindRequested) {
+ mBindRequested = false;
+ mContext.unbindService(this);
+ }
+ mRemoteViewsFactory = null;
}
- public synchronized boolean isConnected() {
- return mIsConnected;
+ private boolean sendNotifyDataSetChange(boolean always) {
+ try {
+ if (always || !mRemoteViewsFactory.isCreated()) {
+ mRemoteViewsFactory.onDataSetChanged();
+ }
+ return true;
+ } catch (RemoteException | RuntimeException e) {
+ Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
+ return false;
+ }
+ }
+
+ private void enqueueDeferredUnbindServiceMessage() {
+ removeMessages(MSG_UNBIND_SERVICE);
+ sendEmptyMessageDelayed(MSG_UNBIND_SERVICE, UNBIND_SERVICE_DELAY);
}
}
@@ -507,7 +547,6 @@
*
*/
private static class FixedSizeRemoteViewsCache {
- private static final String TAG = "FixedSizeRemoteViewsCache";
// The meta data related to all the RemoteViews, ie. count, is stable, etc.
// The meta data objects are made final so that they can be locked on independently
@@ -671,7 +710,7 @@
}
}
- int count = 0;
+ int count;
synchronized (mMetaData) {
count = mMetaData.count;
}
@@ -786,9 +825,11 @@
// Initialize the worker thread
mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
mWorkerThread.start();
- mWorkerQueue = new Handler(mWorkerThread.getLooper());
- mMainQueue = new Handler(Looper.myLooper(), this);
+ mMainHandler = new Handler(Looper.myLooper(), this);
+ mServiceHandler = new RemoteServiceHandler(mWorkerThread.getLooper(), this,
+ context.getApplicationContext());
mAsyncViewLoadExecutor = useAsyncLoader ? new HandlerThreadExecutor(mWorkerThread) : null;
+ mCallback = callback;
if (sCacheRemovalThread == null) {
sCacheRemovalThread = new HandlerThread("RemoteViewsAdapter-cachePruner");
@@ -796,10 +837,6 @@
sCacheRemovalQueue = new Handler(sCacheRemovalThread.getLooper());
}
- // Initialize the cache and the service connection on startup
- mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback);
- mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
-
RemoteViewsCacheKey key = new RemoteViewsCacheKey(new Intent.FilterComparison(mIntent),
mAppWidgetId);
@@ -814,7 +851,7 @@
}
}
} else {
- mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize);
+ mCache = new FixedSizeRemoteViewsCache(DEFAULT_CACHE_SIZE);
}
if (!mDataReady) {
requestBindService();
@@ -825,9 +862,8 @@
@Override
protected void finalize() throws Throwable {
try {
- if (mWorkerThread != null) {
- mWorkerThread.quit();
- }
+ mServiceHandler.unbindNow();
+ mWorkerThread.quit();
} finally {
super.finalize();
}
@@ -864,16 +900,13 @@
sCachedRemoteViewsCaches.put(key, mCache);
}
- Runnable r = new Runnable() {
- @Override
- public void run() {
- synchronized (sCachedRemoteViewsCaches) {
- if (sCachedRemoteViewsCaches.containsKey(key)) {
- sCachedRemoteViewsCaches.remove(key);
- }
- if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) {
- sRemoteViewsCacheRemoveRunnables.remove(key);
- }
+ Runnable r = () -> {
+ synchronized (sCachedRemoteViewsCaches) {
+ if (sCachedRemoteViewsCaches.containsKey(key)) {
+ sCachedRemoteViewsCaches.remove(key);
+ }
+ if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) {
+ sRemoteViewsCacheRemoveRunnables.remove(key);
}
}
};
@@ -882,54 +915,8 @@
}
}
- private void loadNextIndexInBackground() {
- mWorkerQueue.post(new Runnable() {
- @Override
- public void run() {
- if (mServiceConnection.isConnected()) {
- // Get the next index to load
- int position = -1;
- synchronized (mCache) {
- position = mCache.getNextIndexToLoad();
- }
- if (position > -1) {
- // Load the item, and notify any existing RemoteViewsFrameLayouts
- updateRemoteViews(position, true);
-
- // Queue up for the next one to load
- loadNextIndexInBackground();
- } else {
- // No more items to load, so queue unbind
- enqueueDeferredUnbindServiceMessage();
- }
- }
- }
- });
- }
-
- private void processException(String method, Exception e) {
- Log.e("RemoteViewsAdapter", "Error in " + method + ": " + e.getMessage());
-
- // If we encounter a crash when updating, we should reset the metadata & cache and trigger
- // a notifyDataSetChanged to update the widget accordingly
- final RemoteViewsMetaData metaData = mCache.getMetaData();
- synchronized (metaData) {
- metaData.reset();
- }
- synchronized (mCache) {
- mCache.reset();
- }
- mMainQueue.post(new Runnable() {
- @Override
- public void run() {
- superNotifyDataSetChanged();
- }
- });
- }
-
- private void updateTemporaryMetaData() {
- IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
-
+ @WorkerThread
+ private void updateTemporaryMetaData(IRemoteViewsFactory factory) {
try {
// get the properties/first view (so that we can use it to
// measure our dummy views)
@@ -953,40 +940,40 @@
tmpMetaData.count = count;
tmpMetaData.loadingTemplate = loadingTemplate;
}
- } catch(RemoteException e) {
- processException("updateMetaData", e);
- } catch(RuntimeException e) {
- processException("updateMetaData", e);
+ } catch (RemoteException | RuntimeException e) {
+ Log.e("RemoteViewsAdapter", "Error in updateMetaData: " + e.getMessage());
+
+ // If we encounter a crash when updating, we should reset the metadata & cache
+ // and trigger a notifyDataSetChanged to update the widget accordingly
+ synchronized (mCache.getMetaData()) {
+ mCache.getMetaData().reset();
+ }
+ synchronized (mCache) {
+ mCache.reset();
+ }
+ mMainHandler.sendEmptyMessage(MSG_MAIN_HANDLER_SUPER_NOTIFY_DATA_SET_CHANGED);
}
}
- private void updateRemoteViews(final int position, boolean notifyWhenLoaded) {
- IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
-
+ @WorkerThread
+ private void updateRemoteViews(IRemoteViewsFactory factory, int position,
+ boolean notifyWhenLoaded) {
// Load the item information from the remote service
- RemoteViews remoteViews = null;
- long itemId = 0;
+ final RemoteViews remoteViews;
+ final long itemId;
try {
remoteViews = factory.getViewAt(position);
itemId = factory.getItemId(position);
- } catch (RemoteException e) {
+
+ if (remoteViews == null) {
+ throw new RuntimeException("Null remoteViews");
+ }
+ } catch (RemoteException | RuntimeException e) {
Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
// Return early to prevent additional work in re-centering the view cache, and
// swapping from the loading view
return;
- } catch (RuntimeException e) {
- Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
- return;
- }
-
- if (remoteViews == null) {
- // If a null view was returned, we break early to prevent it from getting
- // into our cache and causing problems later. The effect is that the child at this
- // position will remain as a loading view until it is updated.
- Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + " null RemoteViews " +
- "returned from RemoteViewsFactory.");
- return;
}
if (remoteViews.mApplication != null) {
@@ -1013,21 +1000,15 @@
}
synchronized (mCache) {
if (viewTypeInRange) {
- int[] visibleWindow = getVisibleWindow(mVisibleWindowLowerBound,
- mVisibleWindowUpperBound, cacheCount);
+ int[] visibleWindow = getVisibleWindow(cacheCount);
// Cache the RemoteViews we loaded
mCache.insert(position, remoteViews, itemId, visibleWindow);
- // Notify all the views that we have previously returned for this index that
- // there is new data for it.
- final RemoteViews rv = remoteViews;
if (notifyWhenLoaded) {
- mMainQueue.post(new Runnable() {
- @Override
- public void run() {
- mRequestedViews.notifyOnRemoteViewsLoaded(position, rv);
- }
- });
+ // Notify all the views that we have previously returned for this index that
+ // there is new data for it.
+ Message.obtain(mMainHandler, MSG_MAIN_HANDLER_REMOTE_VIEWS_LOADED, position, 0,
+ remoteViews).sendToTarget();
}
} else {
// We need to log an error here, as the the view type count specified by the
@@ -1066,7 +1047,7 @@
}
public int getItemViewType(int position) {
- int typeId = 0;
+ final int typeId;
synchronized (mCache) {
if (mCache.containsMetaDataAt(position)) {
typeId = mCache.getMetaDataAt(position).typeId;
@@ -1097,14 +1078,13 @@
synchronized (mCache) {
RemoteViews rv = mCache.getRemoteViewsAt(position);
boolean isInCache = (rv != null);
- boolean isConnected = mServiceConnection.isConnected();
boolean hasNewItems = false;
if (convertView != null && convertView instanceof RemoteViewsFrameLayout) {
mRequestedViews.removeView((RemoteViewsFrameLayout) convertView);
}
- if (!isInCache && !isConnected) {
+ if (!isInCache) {
// Requesting bind service will trigger a super.notifyDataSetChanged(), which will
// in turn trigger another request to getView()
requestBindService();
@@ -1124,7 +1104,9 @@
if (isInCache) {
// Apply the view synchronously if possible, to avoid flickering
layout.onRemoteViewsLoaded(rv, mRemoteViewsOnClickHandler, false);
- if (hasNewItems) loadNextIndexInBackground();
+ if (hasNewItems) {
+ mServiceHandler.sendEmptyMessage(MSG_LOAD_NEXT_ITEM);
+ }
} else {
// If the views is not loaded, apply the loading view. If the loading view doesn't
// exist, the layout will create a default view based on the firstView height.
@@ -1134,7 +1116,7 @@
false);
mRequestedViews.add(position, layout);
mCache.queueRequestedPositionToLoad(position);
- loadNextIndexInBackground();
+ mServiceHandler.sendEmptyMessage(MSG_LOAD_NEXT_ITEM);
}
return layout;
}
@@ -1158,69 +1140,12 @@
return getCount() <= 0;
}
- private void onNotifyDataSetChanged() {
- // Complete the actual notifyDataSetChanged() call initiated earlier
- IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
- try {
- factory.onDataSetChanged();
- } catch (RemoteException e) {
- Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
-
- // Return early to prevent from further being notified (since nothing has
- // changed)
- return;
- } catch (RuntimeException e) {
- Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
- return;
- }
-
- // Flush the cache so that we can reload new items from the service
- synchronized (mCache) {
- mCache.reset();
- }
-
- // Re-request the new metadata (only after the notification to the factory)
- updateTemporaryMetaData();
- int newCount;
- int[] visibleWindow;
- synchronized(mCache.getTemporaryMetaData()) {
- newCount = mCache.getTemporaryMetaData().count;
- visibleWindow = getVisibleWindow(mVisibleWindowLowerBound,
- mVisibleWindowUpperBound, newCount);
- }
-
- // Pre-load (our best guess of) the views which are currently visible in the AdapterView.
- // This mitigates flashing and flickering of loading views when a widget notifies that
- // its data has changed.
- for (int i: visibleWindow) {
- // Because temporary meta data is only ever modified from this thread (ie.
- // mWorkerThread), it is safe to assume that count is a valid representation.
- if (i < newCount) {
- updateRemoteViews(i, false);
- }
- }
-
- // Propagate the notification back to the base adapter
- mMainQueue.post(new Runnable() {
- @Override
- public void run() {
- synchronized (mCache) {
- mCache.commitTemporaryMetaData();
- }
-
- superNotifyDataSetChanged();
- enqueueDeferredUnbindServiceMessage();
- }
- });
-
- // Reset the notify flagflag
- mNotifyDataSetChangedAfterOnServiceConnected = false;
- }
-
/**
* Returns a sorted array of all integers between lower and upper.
*/
- private int[] getVisibleWindow(int lower, int upper, int count) {
+ private int[] getVisibleWindow(int count) {
+ int lower = mVisibleWindowLowerBound;
+ int upper = mVisibleWindowUpperBound;
// In the case that the window is invalid or uninitialized, return an empty window.
if ((lower == 0 && upper == 0) || lower < 0 || upper < 0) {
return new int[0];
@@ -1250,23 +1175,8 @@
}
public void notifyDataSetChanged() {
- // Dequeue any unbind messages
- mMainQueue.removeMessages(sUnbindServiceMessageType);
-
- // If we are not connected, queue up the notifyDataSetChanged to be handled when we do
- // connect
- if (!mServiceConnection.isConnected()) {
- mNotifyDataSetChangedAfterOnServiceConnected = true;
- requestBindService();
- return;
- }
-
- mWorkerQueue.post(new Runnable() {
- @Override
- public void run() {
- onNotifyDataSetChanged();
- }
- });
+ mServiceHandler.removeMessages(MSG_UNBIND_SERVICE);
+ mServiceHandler.sendEmptyMessage(MSG_NOTIFY_DATA_SET_CHANGED);
}
void superNotifyDataSetChanged() {
@@ -1275,35 +1185,38 @@
@Override
public boolean handleMessage(Message msg) {
- boolean result = false;
switch (msg.what) {
- case sUnbindServiceMessageType:
- if (mServiceConnection.isConnected()) {
- mServiceConnection.unbind(mContext, mAppWidgetId, mIntent);
+ case MSG_MAIN_HANDLER_COMMIT_METADATA: {
+ mCache.commitTemporaryMetaData();
+ return true;
}
- result = true;
- break;
- default:
- break;
+ case MSG_MAIN_HANDLER_SUPER_NOTIFY_DATA_SET_CHANGED: {
+ superNotifyDataSetChanged();
+ return true;
+ }
+ case MSG_MAIN_HANDLER_REMOTE_ADAPTER_CONNECTED: {
+ if (mCallback != null) {
+ mCallback.onRemoteAdapterConnected();
+ }
+ return true;
+ }
+ case MSG_MAIN_HANDLER_REMOTE_ADAPTER_DISCONNECTED: {
+ if (mCallback != null) {
+ mCallback.onRemoteAdapterDisconnected();
+ }
+ return true;
+ }
+ case MSG_MAIN_HANDLER_REMOTE_VIEWS_LOADED: {
+ mRequestedViews.notifyOnRemoteViewsLoaded(msg.arg1, (RemoteViews) msg.obj);
+ return true;
+ }
}
- return result;
+ return false;
}
- private void enqueueDeferredUnbindServiceMessage() {
- // Remove any existing deferred-unbind messages
- mMainQueue.removeMessages(sUnbindServiceMessageType);
- mMainQueue.sendEmptyMessageDelayed(sUnbindServiceMessageType, sUnbindServiceDelay);
- }
-
- private boolean requestBindService() {
- // Try binding the service (which will start it if it's not already running)
- if (!mServiceConnection.isConnected()) {
- mServiceConnection.bind(mContext, mAppWidgetId, mIntent);
- }
-
- // Remove any existing deferred-unbind messages
- mMainQueue.removeMessages(sUnbindServiceMessageType);
- return mServiceConnection.isConnected();
+ private void requestBindService() {
+ mServiceHandler.removeMessages(MSG_UNBIND_SERVICE);
+ Message.obtain(mServiceHandler, MSG_REQUEST_BIND, mAppWidgetId, 0, mIntent).sendToTarget();
}
private static class HandlerThreadExecutor implements Executor {
@@ -1331,7 +1244,7 @@
remoteViews = views;
float density = context.getResources().getDisplayMetrics().density;
- defaultHeight = Math.round(sDefaultLoadingViewHeight * density);
+ defaultHeight = Math.round(DEFAULT_LOADING_VIEW_HEIGHT * density);
}
public void loadFirstViewHeight(
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ce80552..d9bc51f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9219,36 +9219,6 @@
}
}
- @Override
- public void invalidate() {
- super.invalidate();
-
- if (mEditor != null) {
- mEditor.invalidateMagnifier();
- }
- }
-
- @Override
- public void invalidate(int l, int t, int r, int b) {
- super.invalidate(l, t, r, b);
-
- if (mEditor != null) {
- mEditor.invalidateMagnifier(
- convertViewToScreenCoord(l, true /* isHorizontal */),
- convertViewToScreenCoord(t, false /* isHorizontal */),
- convertViewToScreenCoord(r, true /* isHorizontal */),
- convertViewToScreenCoord(b, false /* isHorizontal */));
- }
- }
-
- float convertViewToScreenCoord(float viewCoord, boolean isHorizontal) {
- final int[] coordinatesOnScreen = new int[2];
- getLocationOnScreen(coordinatesOnScreen);
- return isHorizontal
- ? viewCoord + getTotalPaddingLeft() - getScrollX() + coordinatesOnScreen[0]
- : viewCoord + getTotalPaddingTop() - getScrollY() + coordinatesOnScreen[1];
- }
-
/**
* @return whether or not the cursor is visible (assuming this TextView is editable)
*
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index caf35b3..a4da6b9c 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -26,6 +26,8 @@
import android.os.Bundle;
import android.os.IBinder;
import android.widget.RemoteViews;
+import android.app.IApplicationThread;
+import android.app.IServiceConnection;
/** {@hide} */
interface IAppWidgetService {
@@ -62,9 +64,9 @@
void setBindAppWidgetPermission(in String packageName, int userId, in boolean permission);
boolean bindAppWidgetId(in String callingPackage, int appWidgetId,
int providerProfileId, in ComponentName providerComponent, in Bundle options);
- void bindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent,
- in IBinder connection);
- void unbindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent);
+ boolean bindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent,
+ IApplicationThread caller, IBinder token, IServiceConnection connection, int flags);
+
int[] getAppWidgetIds(in ComponentName providerComponent);
boolean isBoundWidgetPackage(String packageName, int userId);
boolean requestPinAppWidget(String packageName, in ComponentName providerComponent,
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 2057292..9cc1959 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -6483,6 +6483,11 @@
}
@Override
+ public Timer getWifiScanTimer() {
+ return mWifiScanTimer;
+ }
+
+ @Override
public int getWifiScanBackgroundCount(int which) {
if (mWifiScanTimer == null || mWifiScanTimer.getSubTimer() == null) {
return 0;
@@ -6509,6 +6514,14 @@
}
@Override
+ public Timer getWifiScanBackgroundTimer() {
+ if (mWifiScanTimer == null) {
+ return null;
+ }
+ return mWifiScanTimer.getSubTimer();
+ }
+
+ @Override
public long getWifiBatchedScanTime(int csphBin, long elapsedRealtimeUs, int which) {
if (csphBin < 0 || csphBin >= NUM_WIFI_BATCHED_SCAN_BINS) return 0;
if (mWifiBatchedScanTimer[csphBin] == null) {
diff --git a/core/java/com/android/internal/util/WakeupMessage.java b/core/java/com/android/internal/util/WakeupMessage.java
index 46098c5..70b6f96 100644
--- a/core/java/com/android/internal/util/WakeupMessage.java
+++ b/core/java/com/android/internal/util/WakeupMessage.java
@@ -47,17 +47,19 @@
protected final int mCmd, mArg1, mArg2;
@VisibleForTesting
protected final Object mObj;
+ private final Runnable mRunnable;
private boolean mScheduled;
public WakeupMessage(Context context, Handler handler,
String cmdName, int cmd, int arg1, int arg2, Object obj) {
- mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ mAlarmManager = getAlarmManager(context);
mHandler = handler;
mCmdName = cmdName;
mCmd = cmd;
mArg1 = arg1;
mArg2 = arg2;
mObj = obj;
+ mRunnable = null;
}
public WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1) {
@@ -73,6 +75,21 @@
this(context, handler, cmdName, cmd, 0, 0, null);
}
+ public WakeupMessage(Context context, Handler handler, String cmdName, Runnable runnable) {
+ mAlarmManager = getAlarmManager(context);
+ mHandler = handler;
+ mCmdName = cmdName;
+ mCmd = 0;
+ mArg1 = 0;
+ mArg2 = 0;
+ mObj = null;
+ mRunnable = runnable;
+ }
+
+ private static AlarmManager getAlarmManager(Context context) {
+ return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ }
+
/**
* Schedule the message to be delivered at the time in milliseconds of the
* {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()} clock and wakeup
@@ -107,7 +124,12 @@
mScheduled = false;
}
if (stillScheduled) {
- Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
+ Message msg;
+ if (mRunnable == null) {
+ msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
+ } else {
+ msg = Message.obtain(mHandler, mRunnable);
+ }
mHandler.dispatchMessage(msg);
msg.recycle();
}
diff --git a/core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl b/core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl
deleted file mode 100644
index 7294124..0000000
--- a/core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2011 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.internal.widget;
-
-import android.os.IBinder;
-
-/** {@hide} */
-oneway interface IRemoteViewsAdapterConnection {
- void onServiceConnected(IBinder service);
- void onServiceDisconnected();
-}
diff --git a/core/java/com/android/internal/widget/Magnifier.java b/core/java/com/android/internal/widget/Magnifier.java
index 9bc0778..6d54d7b 100644
--- a/core/java/com/android/internal/widget/Magnifier.java
+++ b/core/java/com/android/internal/widget/Magnifier.java
@@ -22,9 +22,7 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.os.Handler;
import android.util.Log;
import android.view.Gravity;
@@ -38,13 +36,15 @@
import com.android.internal.R;
import com.android.internal.util.Preconditions;
+import java.util.Timer;
+import java.util.TimerTask;
+
/**
* Android magnifier widget. Can be used by any view which is attached to window.
*/
public final class Magnifier {
private static final String LOG_TAG = "magnifier";
- // Use this to specify that a previous configuration value does not exist.
- private static final int INEXISTENT_PREVIOUS_CONFIG_VALUE = -1;
+ private static final int MAGNIFIER_REFRESH_RATE_MS = 33; // ~30fps
// The view for which this magnifier is attached.
private final View mView;
// The window containing the magnifier.
@@ -62,15 +62,10 @@
// The callback of the pixel copy request will be invoked on this Handler when
// the copy is finished.
private final Handler mPixelCopyHandler = Handler.getMain();
-
- private RectF mTmpRectF;
-
- // Variables holding previous states, used for detecting redundant calls and invalidation.
- private Point mPrevStartCoordsOnScreen = new Point(
- INEXISTENT_PREVIOUS_CONFIG_VALUE, INEXISTENT_PREVIOUS_CONFIG_VALUE);
- private PointF mPrevCenterCoordsOnScreen = new PointF(
- INEXISTENT_PREVIOUS_CONFIG_VALUE, INEXISTENT_PREVIOUS_CONFIG_VALUE);
- private float mPrevScale = INEXISTENT_PREVIOUS_CONFIG_VALUE;
+ // Current magnification scale.
+ private float mScale;
+ // Timer used to schedule the copy task.
+ private Timer mTimer;
/**
* Initializes a magnifier.
@@ -82,6 +77,7 @@
mView = Preconditions.checkNotNull(view);
final Context context = mView.getContext();
final View content = LayoutInflater.from(context).inflate(R.layout.magnifier, null);
+ content.findViewById(R.id.magnifier_inner).setClipToOutline(true);
mWindowWidth = context.getResources().getDimensionPixelSize(R.dimen.magnifier_width);
mWindowHeight = context.getResources().getDimensionPixelSize(R.dimen.magnifier_height);
final float elevation = context.getResources().getDimension(R.dimen.magnifier_elevation);
@@ -101,15 +97,15 @@
/**
* Shows the magnifier on the screen.
*
- * @param centerXOnScreen horizontal coordinate of the center point of the magnifier source. The
- * lower end is clamped to 0
- * @param centerYOnScreen vertical coordinate of the center point of the magnifier source. The
- * lower end is clamped to 0
+ * @param xPosInView horizontal coordinate of the center point of the magnifier source relative
+ * to the view. The lower end is clamped to 0
+ * @param yPosInView vertical coordinate of the center point of the magnifier source
+ * relative to the view. The lower end is clamped to 0
* @param scale the scale at which the magnifier zooms on the source content. The
* lower end is clamped to 1 and the higher end to 4
*/
- public void show(@FloatRange(from=0) float centerXOnScreen,
- @FloatRange(from=0) float centerYOnScreen,
+ public void show(@FloatRange(from=0) float xPosInView,
+ @FloatRange(from=0) float yPosInView,
@FloatRange(from=1, to=4) float scale) {
if (scale > 4) {
scale = 4;
@@ -119,27 +115,29 @@
scale = 1;
}
- if (centerXOnScreen < 0) {
- centerXOnScreen = 0;
+ if (xPosInView < 0) {
+ xPosInView = 0;
}
- if (centerYOnScreen < 0) {
- centerYOnScreen = 0;
+ if (yPosInView < 0) {
+ yPosInView = 0;
}
- showInternal(centerXOnScreen, centerYOnScreen, scale, false);
- }
-
- private void showInternal(@FloatRange(from=0) float centerXOnScreen,
- @FloatRange(from=0) float centerYOnScreen,
- @FloatRange(from=1, to=4) float scale,
- boolean forceShow) {
- if (mPrevScale != scale) {
+ if (mScale != scale) {
resizeBitmap(scale);
- mPrevScale = scale;
}
- configureCoordinates(centerXOnScreen, centerYOnScreen);
- maybePerformPixelCopy(scale, forceShow);
+ mScale = scale;
+ configureCoordinates(xPosInView, yPosInView);
+
+ if (mTimer == null) {
+ mTimer = new Timer();
+ mTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ performPixelCopy();
+ }
+ }, 0 /* delay */, MAGNIFIER_REFRESH_RATE_MS);
+ }
if (mWindow.isShowing()) {
mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
@@ -148,9 +146,6 @@
mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY,
mWindowCoords.x, mWindowCoords.y);
}
-
- mPrevCenterCoordsOnScreen.x = centerXOnScreen;
- mPrevCenterCoordsOnScreen.y = centerYOnScreen;
}
/**
@@ -159,36 +154,10 @@
public void dismiss() {
mWindow.dismiss();
- mPrevStartCoordsOnScreen.x = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- mPrevStartCoordsOnScreen.y = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- mPrevCenterCoordsOnScreen.x = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- mPrevCenterCoordsOnScreen.y = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- mPrevScale = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- }
-
- /**
- * Forces the magnifier to update content by taking and showing a new snapshot using the
- * previous coordinates. It does this only if the magnifier is showing and the dirty rectangle
- * intersects the rectangle which holds the content to be magnified.
- *
- * @param dirtyRectOnScreen the rectangle representing the screen bounds of the dirty region
- */
- public void invalidate(RectF dirtyRectOnScreen) {
- if (mWindow.isShowing() && mPrevCenterCoordsOnScreen.x != INEXISTENT_PREVIOUS_CONFIG_VALUE
- && mPrevCenterCoordsOnScreen.y != INEXISTENT_PREVIOUS_CONFIG_VALUE
- && mPrevScale != INEXISTENT_PREVIOUS_CONFIG_VALUE) {
- // Update the current showing RectF.
- mTmpRectF = new RectF(mPrevStartCoordsOnScreen.x,
- mPrevStartCoordsOnScreen.y,
- mPrevStartCoordsOnScreen.x + mBitmap.getWidth(),
- mPrevStartCoordsOnScreen.y + mBitmap.getHeight());
-
- // Update only if we are currently showing content that has been declared as invalid.
- if (RectF.intersects(dirtyRectOnScreen, mTmpRectF)) {
- // Update the contents shown in the magnifier.
- showInternal(mPrevCenterCoordsOnScreen.x, mPrevCenterCoordsOnScreen.y, mPrevScale,
- true /* forceShow */);
- }
+ if (mTimer != null) {
+ mTimer.cancel();
+ mTimer.purge();
+ mTimer = null;
}
}
@@ -213,7 +182,12 @@
getImageView().setImageBitmap(mBitmap);
}
- private void configureCoordinates(float posXOnScreen, float posYOnScreen) {
+ private void configureCoordinates(float xPosInView, float yPosInView) {
+ final int[] coordinatesOnScreen = new int[2];
+ mView.getLocationOnScreen(coordinatesOnScreen);
+ final float posXOnScreen = xPosInView + coordinatesOnScreen[0];
+ final float posYOnScreen = yPosInView + coordinatesOnScreen[1];
+
mCenterZoomCoords.x = (int) posXOnScreen;
mCenterZoomCoords.y = (int) posYOnScreen;
@@ -223,7 +197,7 @@
mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalMagnifierOffset;
}
- private void maybePerformPixelCopy(final float scale, final boolean forceShow) {
+ private void performPixelCopy() {
final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
int rawStartX = mCenterZoomCoords.x - mBitmap.getWidth() / 2;
@@ -234,13 +208,6 @@
rawStartX = mView.getWidth() - mBitmap.getWidth();
}
- if (!forceShow && rawStartX == mPrevStartCoordsOnScreen.x
- && startY == mPrevStartCoordsOnScreen.y
- && scale == mPrevScale) {
- // Skip, we are already showing the desired content.
- return;
- }
-
final int startX = rawStartX;
final ViewRootImpl viewRootImpl = mView.getViewRootImpl();
@@ -251,11 +218,7 @@
new Rect(startX, startY, startX + mBitmap.getWidth(),
startY + mBitmap.getHeight()),
mBitmap,
- result -> {
- getImageView().invalidate();
- mPrevStartCoordsOnScreen.x = startX;
- mPrevStartCoordsOnScreen.y = startY;
- },
+ result -> getImageView().invalidate(),
mPixelCopyHandler);
} else {
Log.d(LOG_TAG, "Could not perform PixelCopy request");
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index c6801bf..9e6985c 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -56,9 +56,9 @@
if (langs != nullptr) {
ScopedUtfChars str(env, langs);
builder = new NativeFamilyBuilder(
- minikin::FontStyle::registerLanguageList(str.c_str()), variant);
+ minikin::FontStyle::registerLocaleList(str.c_str()), variant);
} else {
- builder = new NativeFamilyBuilder(minikin::FontStyle::registerLanguageList(""), variant);
+ builder = new NativeFamilyBuilder(minikin::FontStyle::registerLocaleList(""), variant);
}
return reinterpret_cast<jlong>(builder);
}
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 93bd16b2..fd62a19f 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -544,7 +544,7 @@
static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
Paint* obj = reinterpret_cast<Paint*>(objHandle);
ScopedUtfChars localesChars(env, locales);
- jint minikinLangListId = minikin::FontStyle::registerLanguageList(localesChars.c_str());
+ jint minikinLangListId = minikin::FontStyle::registerLocaleList(localesChars.c_str());
obj->setMinikinLangListId(minikinLangListId);
return minikinLangListId;
}
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index c3f9bf7..1efff7f 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -765,6 +765,14 @@
if (startPos > totalRows) {
ALOGE("startPos %d > actual rows %d", startPos, totalRows);
}
+ if (totalRows > 0 && addedRows == 0) {
+ String8 msg;
+ msg.appendFormat("Row too big to fit into CursorWindow requiredPos=%d, totalRows=%d",
+ requiredPos, totalRows);
+ throw_sqlite3_exception(env, SQLITE_TOOBIG, NULL, msg.string());
+ return 0;
+ }
+
jlong result = jlong(startPos) << 32 | jlong(totalRows);
return result;
}
diff --git a/core/proto/android/app/jobparameters.proto b/core/proto/android/app/jobparameters.proto
new file mode 100644
index 0000000..4f6a2a2
--- /dev/null
+++ b/core/proto/android/app/jobparameters.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 = "proto2";
+option java_multiple_files = true;
+
+package android.app;
+
+/**
+ * An android.app.JobParameters object.
+ */
+message JobParametersProto {
+ enum CancelReason {
+ REASON_CANCELLED = 0;
+ REASON_CONSTRAINTS_NOT_SATISFIED = 1;
+ REASON_PREEMPT = 2;
+ REASON_TIMEOUT = 3;
+ REASON_DEVICE_IDLE = 4;
+ }
+}
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 38879c0..c33c0a0 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -19,6 +19,8 @@
package android.os;
+import "frameworks/base/core/proto/android/app/jobparameters.proto";
+import "frameworks/base/core/proto/android/os/powermanager.proto";
import "frameworks/base/core/proto/android/telephony/signalstrength.proto";
message BatteryStatsProto {
@@ -356,11 +358,12 @@
optional PowerUseSummary power_use_summary = 18;
message ResourcePowerManager {
+ // Either StateName or StateName.VoterName.
optional string name = 1;
optional TimerProto total = 2;
optional TimerProto screen_off = 3;
}
- optional ResourcePowerManager resource_power_manager = 19;
+ repeated ResourcePowerManager resource_power_manager = 19;
message ScreenBrightness {
enum Name {
@@ -436,12 +439,332 @@
}
message TimerProto {
+ // This may be an apportioned time.
optional int64 duration_ms = 1;
optional int64 count = 2;
+ // The max duration if it is being tracked. Not all Timer subclasses
+ // track the max duration.
+ optional int64 max_duration_ms = 3;
+ // The current time the timer has been active, if it is being tracked.
+ // Not all Timer subclasses track the current duration.
+ optional int64 current_duration_ms = 4;
+ // The total cumulative duration (i.e. sum of past durations) that this timer
+ // has been on since reset. This may differ from duration_ms since, depending
+ // on the Timer, getTotalTimeLocked may represent the total 'blamed' or
+ // 'pooled' time, rather than the actual time. By contrast, total_duration_ms
+ // always gives the actual total time. Not all Timer subclasses track the
+ // total duration.
+ optional int64 total_duration_ms = 5;
}
message UidProto {
// Combination of app ID and user ID.
optional int32 uid = 1;
- repeated string package_names = 2;
+
+ // The statistics associated with a particular package.
+ message Package {
+ optional string name = 1;
+
+ message Service {
+ optional string name = 1;
+ // Time spent started.
+ optional int64 start_duration_ms = 2;
+ optional int32 start_count = 3;
+ optional int32 launch_count = 4;
+ }
+ repeated Service services = 2;
+ }
+ repeated Package packages = 2;
+
+ optional ControllerActivityProto bluetooth_controller = 3;
+ optional ControllerActivityProto modem_controller = 4;
+ optional ControllerActivityProto wifi_controller = 5;
+
+ // Bluetooth misc data.
+ message BluetoothMisc {
+ // Duration spent BLE scanning blamed on this App (i.e. apportioned to this
+ // app amongst all apps doing BLE scanning; see explanation of 'apportioned'
+ // in App's comment).
+ optional TimerProto apportioned_ble_scan = 1;
+ // Background times aren't apportioned.
+ optional TimerProto background_ble_scan = 2;
+ // Running unoptimized BLE scanning, as defined by Bluetooth's
+ // AppScanStats.recordScanStart. As of May 2017, these are unfiltered,
+ // non-opportunistic, non-first-match scans. Durations are not
+ // pooled/apportioned.
+ optional TimerProto unoptimized_ble_scan = 3;
+ // Running unoptimized BLE scanning when app is in background. Durations are
+ // not pooled/apportioned.
+ optional TimerProto background_unoptimized_ble_scan = 4;
+ // Count of results returned by BLE scanning.
+ optional int32 ble_scan_result_count = 5;
+ // Count of results returned by BLE scans when app is in background.
+ // (Included in ble_scan_result_count.)
+ optional int32 background_ble_scan_result_count = 6;
+ }
+ optional BluetoothMisc bluetooth_misc = 6;
+
+ message Cpu {
+ // Total CPU time with processes executing in userspace. Summed up across
+ // multiple cores.
+ optional int64 user_duration_ms = 1;
+ // Total CPU time with processes executing kernel syscalls. Summed up across
+ // multiple cores.
+ optional int64 system_duration_ms = 2;
+
+ // CPU time broken down by CPU frequency (go/cpu-battery-metrics).
+ //
+ // These are real CPU time measurement from the kernel, so their sum can
+ // be different from the sum of user_duration_millis and
+ // system_duration_millis, which are just approximations. Data is not
+ // tracked when device is charging.
+ message ByFrequency {
+ // Index of the frequency in system.cpu_frequency. It starts from 1, to
+ // make it easier to analyze.
+ optional int32 frequency_index = 1;
+ // CPU time in milliseconds.
+ optional int64 total_duration_ms = 2;
+ // Screen-off CPU time in milliseconds.
+ optional int64 screen_off_duration_ms = 3;
+ }
+ repeated ByFrequency by_frequency = 3;
+ }
+ optional Cpu cpu = 7;
+
+ // Duration is pooled/apportioned.
+ optional TimerProto audio = 8;
+ // Duration is pooled/apportioned.
+ optional TimerProto camera = 9;
+ // Duration is pooled/apportioned.
+ optional TimerProto flashlight = 10;
+ // Duration is not pooled/apportioned.
+ optional TimerProto foreground_activity = 11;
+ // Duration is not pooled/apportioned.
+ optional TimerProto foreground_service = 12;
+ // Duration is not pooled/apportioned.
+ optional TimerProto vibrator = 13;
+ // Duration is pooled/apportioned.
+ optional TimerProto video = 14;
+
+ message Job {
+ optional string name = 1;
+ // Job times aren't apportioned.
+ optional TimerProto total = 2;
+ optional TimerProto background = 3;
+ }
+ repeated Job jobs = 15;
+
+ message JobCompletion {
+ // Job name.
+ optional string name = 1;
+
+ message ReasonCount {
+ optional android.app.JobParametersProto.CancelReason name = 1;
+ optional int32 count = 2;
+ }
+ repeated ReasonCount reason_count = 2;
+ };
+ repeated JobCompletion job_completion = 16;
+
+ message Network {
+ // Mobile data traffic (total, background + foreground).
+ optional int64 mobile_bytes_rx = 1;
+ optional int64 mobile_bytes_tx = 2;
+ // Wifi data traffic (total, background + foreground).
+ optional int64 wifi_bytes_rx = 3;
+ optional int64 wifi_bytes_tx = 4;
+ // Bluetooth data traffic (total, background + foreground).
+ optional int64 bt_bytes_rx = 5;
+ optional int64 bt_bytes_tx = 6;
+ // In packets (total, background + foreground).
+ optional int64 mobile_packets_rx = 7;
+ optional int64 mobile_packets_tx = 8;
+ optional int64 wifi_packets_rx = 9;
+ optional int64 wifi_packets_tx = 10;
+ // Radio active duration.
+ optional int64 mobile_active_duration_ms = 11;
+ optional int32 mobile_active_count = 12;
+ // Number of times the app woke up the mobile radio.
+ optional int32 mobile_wakeup_count = 13;
+ // Number of times the app woke up the wifi radio.
+ optional int32 wifi_wakeup_count = 14;
+ // Mobile data traffic in the background only, included in total above.
+ optional int64 mobile_bytes_bg_rx = 15;
+ optional int64 mobile_bytes_bg_tx = 16;
+ // Wifi data traffic in the background only, included in total above.
+ optional int64 wifi_bytes_bg_rx = 17;
+ optional int64 wifi_bytes_bg_tx = 18;
+ // In packets (background only, included in total packets above).
+ optional int64 mobile_packets_bg_rx = 19;
+ optional int64 mobile_packets_bg_tx = 20;
+ optional int64 wifi_packets_bg_rx = 21;
+ optional int64 wifi_packets_bg_tx = 22;
+ };
+ optional Network network = 17;
+
+ // TODO: combine System and App messages?
+ message PowerUseItem {
+ // Estimated power use in mAh.
+ optional double computed_power_mah = 1;
+ // Starting in Oreo, Battery Settings has two modes to display the battery
+ // info. The first is "app usage list". In this mode, items with should_hide
+ // enabled are hidden.
+ optional bool should_hide = 2;
+ // Smeared power from screen usage. Screen usage power is split and smeared
+ // among apps, based on activity time.
+ optional double screen_power_mah = 3;
+ // Smeared power using proportional method. Power usage from hidden sippers
+ // is smeared to all apps proportionally (except for screen usage).
+ optional double proportional_smear_mah = 4;
+ };
+ optional PowerUseItem power_use_item = 18;
+
+ // Durations are not pooled/apportioned.
+ message Process {
+ optional string name = 1;
+ // Time spent executing in user code.
+ optional int64 user_duration_ms = 2;
+ // Time spent executing in kernel code.
+ optional int64 system_duration_ms = 3;
+ // Time the process was running in the foreground.
+ optional int64 foreground_duration_ms = 4;
+ // Number of times the process has been started.
+ optional int32 start_count = 5;
+ // Number of times the process has had an ANR.
+ optional int32 anr_count = 6;
+ // Number of times the process has crashed.
+ optional int32 crash_count = 7;
+ };
+ repeated Process process = 19;
+
+ message StateTime {
+ // All of these (non-deprecated) states are mutually exclusive and can be
+ // added together to find the total time a uid has had any processes running
+ // at all.
+
+ // In approximate order or priority (top being what the framework considers
+ // most important and is thus least likely to kill when resources are
+ // needed:
+ // top > foreground service > top sleeping > foreground > background > cache
+ enum State {
+ // Time this uid has any processes in the top state (or above such as
+ // persistent).
+ PROCESS_STATE_TOP = 0;
+ // Time this uid has any process with a started out bound foreground
+ // service, but none in the "top" state.
+ PROCESS_STATE_FOREGROUND_SERVICE = 1;
+ // Time this uid has any process that is top while the device is sleeping,
+ // but none in the "foreground service" or better state. Sleeping is
+ // mostly screen off, but also includes the time when the screen is on but
+ // the device has not yet been unlocked.
+ PROCESS_STATE_TOP_SLEEPING = 2;
+ // Time this uid has any process in an active foreground state, but none
+ // in the "top sleeping" or better state.
+ PROCESS_STATE_FOREGROUND = 3;
+ // Time this uid has any process in an active background state, but none
+ // in the "foreground" or better state.
+ PROCESS_STATE_BACKGROUND = 4;
+ // Time this uid has any processes that are sitting around cached, not in
+ // one of the other active states.
+ PROCESS_STATE_CACHED = 5;
+ }
+ optional State state = 1;
+ optional int64 duration_ms = 2;
+ }
+ repeated StateTime states = 20;
+
+ message Sensor {
+ optional int32 id = 1;
+ optional TimerProto apportioned = 2;
+ // Background times aren't apportioned.
+ optional TimerProto background = 3;
+ }
+ repeated Sensor sensors = 21;
+
+ message Sync {
+ optional string name = 1;
+ // Sync times aren't apportioned.
+ optional TimerProto total = 2;
+ optional TimerProto background = 3;
+ }
+ repeated Sync syncs = 22;
+
+ message UserActivity {
+ optional android.os.PowerManagerProto.UserActivityEvent name = 1;
+ optional int32 count = 2;
+ };
+ repeated UserActivity user_activity = 23;
+
+ // Aggregated wakelock data for an app overall, across all of its wakelocks.
+ // The Wakelock message holds data about each *individual* wakelock, but it
+ // cannot be used to ascertain the aggregated time the app spent holding
+ // wakelocks, since merely summing Wakelock data will either underestimate (in
+ // the case of wakelock.partial.duration_ms) or overestimate (in the case of
+ // wakelock.partial.total_duration_ms) the total time, due to overlapping
+ // wakelocks. AggregatedWakelock, on the other hand, holds overall per-app
+ // wakelock data.
+ message AggregatedWakelock {
+ // The total duration that the app spent holding partial wakelocks.
+ // It includes both foreground + background use.
+ optional int64 partial_duration_ms = 1;
+ // The total duration that the app spent holding partial wakelocks while the
+ // app was in the background. Subtracting from partial_duration_ms will
+ // yield foreground usage.
+ optional int64 background_partial_duration_ms = 2;
+ };
+ optional AggregatedWakelock aggregated_wakelock = 24;
+
+ message Wakelock {
+ optional string name = 1;
+
+ // Full wakelocks keep the screen on. Based on
+ // PowerManager.SCREEN_BRIGHT_WAKE_LOCK (deprecated in API 13) and
+ // PowerManager.SCREEN_DIM_WAKE_LOCK (deprecated in API 17). Current, max,
+ // and total durations are not tracked for full wakelocks.
+ optional TimerProto full = 2;
+
+ // Partial wakelocks ensure the CPU is running while allowing the screen
+ // to turn off. Based on PowerManager.PARTIAL_WAKE_LOCK.
+ // Partial wakelock metrics are only recorded when the device is unplugged
+ // *and* the screen is off. Current, max, and total durations are tracked
+ // for partial wakelocks.
+ optional TimerProto partial = 3;
+
+ // These fields are for tracking partial wakelocks (see above), but only
+ // the time the wakelock was held while the app was in a background state.
+ // Since all background tracking is 'actual', not 'apportioned',
+ // background_partial.duration_ms is identical to
+ // background_partial.total_duration_ms.
+ optional TimerProto background_partial = 4;
+
+ // Window wakelocks keep the screen on. Current, max, and total durations
+ // are not tracked for window wakelocks.
+ optional TimerProto window = 5;
+ };
+ repeated Wakelock wakelocks = 25;
+
+ message WakeupAlarm {
+ // Wakeup alarm name.
+ optional string name = 1;
+ // Only includes counts when screen-off (& on battery).
+ optional int32 count = 2;
+ }
+ repeated WakeupAlarm wakeup_alarm = 26;
+
+ message Wifi {
+ // Duration holding Wifi-lock. This time is apportioned.
+ optional int64 full_wifi_lock_duration_ms = 1;
+ // Duration running Wifi. This time is apportioned.
+ optional int64 running_duration_ms = 2;
+ // Duration performing Wifi-scan blamed on this App (i.e. apportioned to
+ // this app amongst all apps doing Wifi-scanning; see explanation of
+ // 'apportioned' in App's comment).
+ optional TimerProto apportioned_scan = 3;
+ // Scans performed when app is in background. (Included in
+ // apportioned_scan). This value is not apportioned. Subtracting
+ // background_scan.total_duration_ms from apportioned_scan.total_duration_ms
+ // will yield foreground usage.
+ optional TimerProto background_scan = 4;
+ };
+ optional Wifi wifi = 27;
}
diff --git a/core/proto/android/os/powermanager.proto b/core/proto/android/os/powermanager.proto
new file mode 100644
index 0000000..3bfe5d6
--- /dev/null
+++ b/core/proto/android/os/powermanager.proto
@@ -0,0 +1,34 @@
+/*
+ * 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 = "proto2";
+option java_multiple_files = true;
+
+package android.os;
+
+message PowerManagerProto {
+ /* User activity events in PowerManager.java. */
+ enum UserActivityEvent {
+ // Unspecified event type.
+ USER_ACTIVITY_EVENT_OTHER = 0;
+ // Button or key pressed or released.
+ USER_ACTIVITY_EVENT_BUTTON = 1;
+ // Touch down, move or up.
+ USER_ACTIVITY_EVENT_TOUCH = 2;
+ // Accessibility taking action on behalf of user.
+ USER_ACTIVITY_EVENT_ACCESSIBILITY = 3;
+ }
+}
diff --git a/core/res/res/layout/magnifier.xml b/core/res/res/layout/magnifier.xml
index 181e5e5..d6cd8b4 100644
--- a/core/res/res/layout/magnifier.xml
+++ b/core/res/res/layout/magnifier.xml
@@ -18,10 +18,17 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="?android:attr/floatingToolbarPopupBackgroundDrawable">
- <ImageView
- android:id="@+id/magnifier_image"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="wrap_content">
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/magnifier_inner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/floatingToolbarPopupBackgroundDrawable"
+ android:elevation="@android:dimen/magnifier_elevation">
+ <ImageView
+ android:id="@+id/magnifier_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </LinearLayout>
</LinearLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dec5fd9..571aad9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3128,4 +3128,7 @@
<!-- Class names of device specific services inheriting com.android.server.SystemService. The
classes are instantiated in the order of the array. -->
<string-array translatable="false" name="config_deviceSpecificSystemServices"></string-array>
+
+ <!-- Component name of media projection permission dialog -->
+ <string name="config_mediaProjectionPermissionDialogComponent" translateable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 14069e7..08e2233 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -521,7 +521,7 @@
<dimen name="content_rect_bottom_clip_allowance">20dp</dimen>
<!-- Magnifier dimensions -->
- <dimen name="magnifier_width">200dp</dimen>
+ <dimen name="magnifier_width">164dp</dimen>
<dimen name="magnifier_height">48dp</dimen>
<dimen name="magnifier_elevation">2dp</dimen>
<dimen name="magnifier_offset">42dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7bbd17e..1627a0e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2479,6 +2479,7 @@
<!-- Magnifier -->
<java-symbol type="id" name="magnifier_image" />
+ <java-symbol type="id" name="magnifier_inner" />
<java-symbol type="layout" name="magnifier" />
<java-symbol type="dimen" name="magnifier_width" />
<java-symbol type="dimen" name="magnifier_height" />
@@ -3123,4 +3124,7 @@
<java-symbol type="string" name="shortcut_restore_not_supported" />
<java-symbol type="string" name="shortcut_restore_signature_mismatch" />
<java-symbol type="string" name="shortcut_restore_unknown_issue" />
+
+ <!-- From media projection -->
+ <java-symbol type="string" name="config_mediaProjectionPermissionDialogComponent" />
</resources>
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
new file mode 100644
index 0000000..06b860a
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
@@ -0,0 +1,380 @@
+/*
+ * 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.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.IServiceConnection;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.database.DataSetObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import com.android.frameworks.coretests.R;
+import com.android.internal.widget.IRemoteViewsFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+
+/**
+ * Tests for RemoteViewsAdapter.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RemoteViewsAdapterTest {
+
+ @Mock AppWidgetManager mAppWidgetManager;
+ @Mock IServiceConnection mIServiceConnection;
+ @Mock RemoteViewsAdapter.RemoteAdapterConnectionCallback mCallback;
+
+ private Handler mMainHandler;
+ private TestContext mContext;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mAppWidgetManager
+ .bindRemoteViewsService(any(), anyInt(), any(), any(), anyInt())).thenReturn(true);
+ mContext = new TestContext();
+ mMainHandler = new Handler(Looper.getMainLooper());
+ }
+
+ @Test
+ public void onRemoteAdapterConnected_after_metadata_loaded() throws Throwable {
+ RemoteViewsAdapter adapter = getOnUiThread(
+ () -> new RemoteViewsAdapter(mContext, new DistinctIntent(), mCallback, false));
+ assertFalse(adapter.isDataReady());
+
+ assertNotNull(mContext.conn.get());
+
+ ViewsFactory factory = new ViewsFactory(1);
+ mContext.sendConnect(factory);
+
+ waitOnHandler(mMainHandler);
+ verify(mCallback, never()).onRemoteAdapterConnected();
+
+ factory.loadingView.set(createViews("loading"));
+ waitOnHandler(mContext.handler.get());
+ waitOnHandler(mMainHandler);
+ verify(mCallback, times(1)).onRemoteAdapterConnected();
+
+ assertEquals((Integer) 1, getOnUiThread(adapter::getCount));
+
+ // Service is unbound
+ assertTrue(isUnboundOrScheduled());
+ }
+
+ @Test
+ public void viewReplaced_after_mainView_loaded() throws Throwable {
+ RemoteViewsAdapter adapter = getOnUiThread(
+ () -> new RemoteViewsAdapter(mContext, new DistinctIntent(), mCallback, false));
+
+ ViewsFactory factory = new ViewsFactory(1);
+ factory.loadingView.set(createViews("loading"));
+ mContext.sendConnect(factory);
+
+ waitOnHandler(mContext.handler.get());
+ waitOnHandler(mMainHandler);
+
+ // Returned view contains the loading text
+ View view = getOnUiThread(() -> adapter.getView(0, null, new FrameLayout(mContext)));
+ ArrayList<View> search = new ArrayList<>();
+ view.findViewsWithText(search, "loading", View.FIND_VIEWS_WITH_TEXT);
+ assertEquals(1, search.size());
+
+ // Send the final remoteViews
+ factory.views[0].set(createViews("updated"));
+ waitOnHandler(mContext.handler.get());
+ waitOnHandler(mMainHandler);
+
+ // Existing view got updated with new text
+ search.clear();
+ view.findViewsWithText(search, "loading", View.FIND_VIEWS_WITH_TEXT);
+ assertTrue(search.isEmpty());
+ view.findViewsWithText(search, "updated", View.FIND_VIEWS_WITH_TEXT);
+ assertEquals(1, search.size());
+
+ // Service is unbound
+ assertTrue(isUnboundOrScheduled());
+ }
+
+ @Test
+ public void notifyDataSetChanged_deferred() throws Throwable {
+ RemoteViewsAdapter adapter = getOnUiThread(
+ () -> new RemoteViewsAdapter(mContext, new DistinctIntent(), mCallback, false));
+
+ ViewsFactory factory = new ViewsFactory(1);
+ factory.loadingView.set(createViews("loading"));
+ mContext.sendConnect(factory);
+
+ waitOnHandler(mContext.handler.get());
+ waitOnHandler(mMainHandler);
+ assertEquals((Integer) 1, getOnUiThread(adapter::getCount));
+
+ // Reset the loading view so that next refresh is blocked
+ factory.loadingView = new LockedValue<>();
+ factory.mCount = 3;
+ DataSetObserver observer = mock(DataSetObserver.class);
+ getOnUiThread(() -> {
+ adapter.registerDataSetObserver(observer);
+ adapter.notifyDataSetChanged();
+ return null;
+ });
+
+ waitOnHandler(mMainHandler);
+ // Still giving the old values
+ verify(observer, never()).onChanged();
+ assertEquals((Integer) 1, getOnUiThread(adapter::getCount));
+
+ factory.loadingView.set(createViews("refreshed"));
+ waitOnHandler(mContext.handler.get());
+ waitOnHandler(mMainHandler);
+
+ // When the service returns new data, UI is updated.
+ verify(observer, times(1)).onChanged();
+ assertEquals((Integer) 3, getOnUiThread(adapter::getCount));
+
+ // Service is unbound
+ assertTrue(isUnboundOrScheduled());
+ }
+
+ @Test
+ public void serviceDisconnected_before_getView() throws Throwable {
+ RemoteViewsAdapter adapter = getOnUiThread(
+ () -> new RemoteViewsAdapter(mContext, new DistinctIntent(), mCallback, false));
+
+ ViewsFactory factory = new ViewsFactory(1);
+ factory.loadingView.set(createViews("loading"));
+ mContext.sendConnect(factory);
+
+ waitOnHandler(mContext.handler.get());
+ waitOnHandler(mMainHandler);
+ verify(mCallback, times(1)).onRemoteAdapterConnected();
+ assertEquals((Integer) 1, getOnUiThread(adapter::getCount));
+
+ // Unbind the service
+ ServiceConnection conn = mContext.conn.get();
+ getOnHandler(mContext.handler.get(), () -> {
+ conn.onServiceDisconnected(null);
+ return null;
+ });
+
+ // Returned view contains the loading text
+ View view = getOnUiThread(() -> adapter.getView(0, null, new FrameLayout(mContext)));
+ ArrayList<View> search = new ArrayList<>();
+ view.findViewsWithText(search, "loading", View.FIND_VIEWS_WITH_TEXT);
+ assertEquals(1, search.size());
+
+ // Unbind is not scheduled
+ assertFalse(isUnboundOrScheduled());
+
+ mContext.sendConnect(factory);
+ waitOnHandler(mContext.handler.get());
+ waitOnHandler(mMainHandler);
+ verify(mCallback, times(2)).onRemoteAdapterConnected();
+ }
+
+ private RemoteViews createViews(String text) {
+ RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.remote_views_text);
+ views.setTextViewText(R.id.text, text);
+ return views;
+ }
+
+ private <T> T getOnUiThread(Supplier<T> supplier) throws Throwable {
+ return getOnHandler(mMainHandler, supplier);
+ }
+
+ private boolean isUnboundOrScheduled() throws Throwable {
+ Handler handler = mContext.handler.get();
+ return getOnHandler(handler, () -> mContext.boundCount == 0
+ || handler.hasMessages(RemoteViewsAdapter.MSG_UNBIND_SERVICE));
+ }
+
+ private static <T> T getOnHandler(Handler handler, Supplier<T> supplier) throws Throwable {
+ LockedValue<T> result = new LockedValue<>();
+ handler.post(() -> result.set(supplier.get()));
+ return result.get();
+ }
+
+ private class TestContext extends ContextWrapper {
+
+ public final LockedValue<ServiceConnection> conn = new LockedValue<>();
+ public final LockedValue<Handler> handler = new LockedValue<>();
+ public int boundCount;
+
+ TestContext() {
+ super(InstrumentationRegistry.getContext());
+ }
+
+ @Override
+ public void unbindService(ServiceConnection conn) {
+ boundCount--;
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ if (Context.APPWIDGET_SERVICE.equals(name)) {
+ return mAppWidgetManager;
+ }
+ return super.getSystemService(name);
+ }
+
+ @Override
+ public IServiceConnection getServiceDispatcher(
+ ServiceConnection conn, Handler handler, int flags) {
+ this.conn.set(conn);
+ this.handler.set(handler);
+ boundCount++;
+ return mIServiceConnection;
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ return this;
+ }
+
+ public void sendConnect(ViewsFactory factory) throws Exception {
+ ServiceConnection connection = conn.get();
+ handler.get().post(() -> connection.onServiceConnected(null, factory.asBinder()));
+ }
+ }
+
+ private static void waitOnHandler(Handler handler) throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+ handler.post(() -> latch.countDown());
+ latch.await(20, TimeUnit.SECONDS);
+ }
+
+ private static class ViewsFactory extends IRemoteViewsFactory.Stub {
+
+ public LockedValue<RemoteViews> loadingView = new LockedValue<>();
+ public LockedValue<RemoteViews>[] views;
+
+ private int mCount;
+
+ ViewsFactory(int count) {
+ mCount = count;
+ views = new LockedValue[count];
+ for (int i = 0; i < count; i++) {
+ views[i] = new LockedValue<>();
+ }
+ }
+
+ @Override
+ public void onDataSetChanged() {}
+
+ @Override
+ public void onDataSetChangedAsync() { }
+
+ @Override
+ public void onDestroy(Intent intent) { }
+
+ @Override
+ public int getCount() throws RemoteException {
+ return mCount;
+ }
+
+ @Override
+ public RemoteViews getViewAt(int position) throws RemoteException {
+ try {
+ return views[position].get();
+ } catch (Exception e) {
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ @Override
+ public RemoteViews getLoadingView() throws RemoteException {
+ try {
+ return loadingView.get();
+ } catch (Exception e) {
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return false;
+ }
+
+ @Override
+ public boolean isCreated() {
+ return false;
+ }
+ }
+
+ private static class DistinctIntent extends Intent {
+
+ @Override
+ public boolean filterEquals(Intent other) {
+ return false;
+ }
+ }
+
+ private static class LockedValue<T> {
+
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private T mValue;
+
+ public void set(T value) {
+ mValue = value;
+ mLatch.countDown();
+ }
+
+ public T get() throws Exception {
+ mLatch.await(10, TimeUnit.SECONDS);
+ return mValue;
+ }
+ }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
index 7935880..734ebef 100644
--- a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
@@ -47,6 +47,7 @@
private static final int TEST_ARG2 = 182;
private static final Object TEST_OBJ = "hello";
+ @Mock Context mContext;
@Mock AlarmManager mAlarmManager;
WakeupMessage mMessage;
// Make a spy so that we can verify calls to it
@@ -86,13 +87,12 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- Context context = mock(Context.class);
- when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
+ when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
// capture the listener for each AlarmManager.setExact call
doNothing().when(mAlarmManager).setExact(anyInt(), anyLong(), any(String.class),
mListenerCaptor.capture(), any(Handler.class));
- mMessage = new WakeupMessage(context, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1,
+ mMessage = new WakeupMessage(mContext, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1,
TEST_ARG2, TEST_OBJ);
}
@@ -168,4 +168,19 @@
verifyMessageDispatchedOnce();
}
+ /**
+ * Verify that a Runnable is scheduled and dispatched.
+ */
+ @Test
+ public void scheduleRunnable() {
+ final long when = 1011;
+ final Runnable runnable = mock(Runnable.class);
+ WakeupMessage dut = new WakeupMessage(mContext, mHandler, TEST_CMD_NAME, runnable);
+ dut.schedule(when);
+ verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(when),
+ eq(TEST_CMD_NAME), any(AlarmManager.OnAlarmListener.class), eq(mHandler));
+ mListenerCaptor.getValue().onAlarm();
+ verify(runnable, times(1)).run();
+ }
+
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 6f2b038..2f9ae57 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -24,6 +24,10 @@
<permission name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" />
</privapp-permissions>
+ <privapp-permissions package="com.android.apps.tag">
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.backupconfirm">
<permission name="android.permission.BACKUP"/>
<permission name="android.permission.CRYPT_KEEPER"/>
@@ -54,6 +58,7 @@
<permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.STATUS_BAR"/>
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
<permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
@@ -353,6 +358,7 @@
<permission name="android.permission.WRITE_DREAM_STATE"/>
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <permission name="com.android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
</privapp-permissions>
<privapp-permissions package="com.android.tv">
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 3b59bb1..588c4de 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -58,6 +58,7 @@
bool Properties::filterOutTestOverhead = false;
bool Properties::disableVsync = false;
+bool Properties::skpCaptureEnabled = false;
static int property_get_int(const char* key, int defaultValue) {
char buf[PROPERTY_VALUE_MAX] = {'\0',};
@@ -128,6 +129,9 @@
filterOutTestOverhead = property_get_bool(PROPERTY_FILTER_TEST_OVERHEAD, false);
+ skpCaptureEnabled = property_get_bool("ro.debuggable", false)
+ && property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false);
+
return (prevDebugLayersUpdates != debugLayersUpdates)
|| (prevDebugOverdraw != debugOverdraw)
|| (prevDebugStencilClip != debugStencilClip);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 0fe4c77..077e7ae 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -165,6 +165,22 @@
*/
#define PROPERTY_RENDERER "debug.hwui.renderer"
+/**
+ * Allows to collect a recording of Skia drawing commands.
+ */
+#define PROPERTY_CAPTURE_SKP_ENABLED "debug.hwui.capture_skp_enabled"
+
+
+/**
+ * Defines how many frames in a sequence to capture.
+ */
+#define PROPERTY_CAPTURE_SKP_FRAMES "debug.hwui.capture_skp_frames"
+
+/**
+ * File name and location, where a SKP recording will be saved.
+ */
+#define PROPERTY_CAPTURE_SKP_FILENAME "debug.hwui.skp_filename"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -254,6 +270,8 @@
// created after changing this.
static bool disableVsync;
+ static bool skpCaptureEnabled;
+
// Used for testing only to change the render pipeline.
#ifdef HWUI_GLES_WRAP_ENABLED
static void overrideRenderPipelineType(RenderPipelineType);
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 3b72a8b..def04e1 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -35,6 +35,10 @@
}
bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) {
+ if (context == nullptr) {
+ SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
+ return false;
+ }
// transform the matrix based on the layer
SkMatrix layerTransform;
layer->getTransform().copyTo(layerTransform);
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 47dee9d..ca93925 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -101,7 +101,7 @@
void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
RenderNode* renderNode = mRenderNode.get();
- if (SkiaPipeline::skpCaptureEnabled()) {
+ if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index cdf4aee..a092cd5 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -112,7 +112,7 @@
const Rect& layerDamage = layers.entries()[i].damage;
- SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
+ SkCanvas* layerCanvas = tryCapture(layerNode->getLayerSurface());
int saveCount = layerCanvas->save();
SkASSERT(saveCount == 1);
@@ -139,9 +139,11 @@
layerCanvas->restoreToCount(saveCount);
mLightCenter = savedLightCenter;
+ endCapture(layerNode->getLayerSurface());
+
// cache the current context so that we can defer flushing it until
// either all the layers have been rendered or the context changes
- GrContext* currentContext = layerCanvas->getGrContext();
+ GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
if (cachedContext.get() != currentContext) {
if (cachedContext.get()) {
cachedContext->flush();
@@ -227,6 +229,91 @@
}
}
+class SkiaPipeline::SavePictureProcessor : public TaskProcessor<bool> {
+public:
+ explicit SavePictureProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {}
+
+ struct SavePictureTask : public Task<bool> {
+ sk_sp<SkData> data;
+ std::string filename;
+ };
+
+ void savePicture(const sk_sp<SkData>& data, const std::string& filename) {
+ sp<SavePictureTask> task(new SavePictureTask());
+ task->data = data;
+ task->filename = filename;
+ TaskProcessor<bool>::add(task);
+ }
+
+ virtual void onProcess(const sp<Task<bool> >& task) override {
+ SavePictureTask* t = static_cast<SavePictureTask*>(task.get());
+
+ if (0 == access(t->filename.c_str(), F_OK)) {
+ task->setResult(false);
+ return;
+ }
+
+ SkFILEWStream stream(t->filename.c_str());
+ if (stream.isValid()) {
+ stream.write(t->data->data(), t->data->size());
+ stream.flush();
+ SkDebugf("SKP Captured Drawing Output (%d bytes) for frame. %s",
+ stream.bytesWritten(), t->filename.c_str());
+ }
+
+ task->setResult(true);
+ }
+};
+
+SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) {
+ if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
+ bool recordingPicture = mCaptureSequence > 0;
+ char prop[PROPERTY_VALUE_MAX] = {'\0'};
+ if (!recordingPicture) {
+ property_get(PROPERTY_CAPTURE_SKP_FILENAME, prop, "0");
+ recordingPicture = prop[0] != '0'
+ && mCapturedFile != prop; //ensure we capture only once per filename
+ if (recordingPicture) {
+ mCapturedFile = prop;
+ mCaptureSequence = property_get_int32(PROPERTY_CAPTURE_SKP_FRAMES, 1);
+ }
+ }
+ if (recordingPicture) {
+ mRecorder.reset(new SkPictureRecorder());
+ return mRecorder->beginRecording(surface->width(), surface->height(), nullptr,
+ SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
+ }
+ }
+ return surface->getCanvas();
+}
+
+void SkiaPipeline::endCapture(SkSurface* surface) {
+ if (CC_UNLIKELY(mRecorder.get())) {
+ sk_sp<SkPicture> picture = mRecorder->finishRecordingAsPicture();
+ surface->getCanvas()->drawPicture(picture);
+ if (picture->approximateOpCount() > 0) {
+ SkDynamicMemoryWStream stream;
+ PngPixelSerializer serializer;
+ picture->serialize(&stream, &serializer);
+
+ //offload saving to file in a different thread
+ if (!mSavePictureProcessor.get()) {
+ TaskManager* taskManager = getTaskManager();
+ mSavePictureProcessor = new SavePictureProcessor(
+ taskManager->canRunTasks() ? taskManager : nullptr);
+ }
+ if (1 == mCaptureSequence) {
+ mSavePictureProcessor->savePicture(stream.detachAsData(), mCapturedFile);
+ } else {
+ mSavePictureProcessor->savePicture(stream.detachAsData(),
+ mCapturedFile + "_" + std::to_string(mCaptureSequence));
+ }
+ mCaptureSequence--;
+ }
+ mRecorder.reset();
+ }
+}
+
void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut,
const Rect &contentDrawBounds, sk_sp<SkSurface> surface) {
@@ -236,44 +323,21 @@
// draw all layers up front
renderLayersImpl(layers, opaque, wideColorGamut);
- // initialize the canvas for the current frame
- SkCanvas* canvas = surface->getCanvas();
-
+ // initialize the canvas for the current frame, that might be a recording canvas if SKP
+ // capture is enabled.
std::unique_ptr<SkPictureRecorder> recorder;
- bool recordingPicture = false;
- char prop[PROPERTY_VALUE_MAX];
- if (skpCaptureEnabled()) {
- property_get("debug.hwui.capture_frame_as_skp", prop, "0");
- recordingPicture = prop[0] != '0' && access(prop, F_OK) != 0;
- if (recordingPicture) {
- recorder.reset(new SkPictureRecorder());
- canvas = recorder->beginRecording(surface->width(), surface->height(),
- nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
- }
- }
+ SkCanvas* canvas = tryCapture(surface.get());
renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas);
- if (skpCaptureEnabled() && recordingPicture) {
- sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture();
- if (picture->approximateOpCount() > 0) {
- SkFILEWStream stream(prop);
- if (stream.isValid()) {
- PngPixelSerializer serializer;
- picture->serialize(&stream, &serializer);
- stream.flush();
- SkDebugf("Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), prop);
- }
- }
- surface->getCanvas()->drawPicture(picture);
- }
+ endCapture(surface.get());
if (CC_UNLIKELY(Properties::debugOverdraw)) {
renderOverdraw(layers, clip, nodes, contentDrawBounds, surface);
}
ATRACE_NAME("flush commands");
- canvas->flush();
+ surface->getCanvas()->flush();
}
namespace {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index dc0b8f6d..67b1878 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -21,6 +21,8 @@
#include "renderthread/IRenderPipeline.h"
#include <SkSurface.h>
+class SkPictureRecorder;
+
namespace android {
namespace uirenderer {
namespace skiapipeline {
@@ -56,9 +58,7 @@
static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
- static void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut);
-
- static bool skpCaptureEnabled() { return false; }
+ void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut);
static float getLightRadius() {
if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) {
@@ -127,12 +127,38 @@
*/
void renderVectorDrawableCache();
+ SkCanvas* tryCapture(SkSurface* surface);
+ void endCapture(SkSurface* surface);
+
std::vector<sk_sp<SkImage>> mPinnedImages;
/**
* populated by prepareTree with dirty VDs
*/
std::vector<VectorDrawableRoot*> mVectorDrawables;
+
+
+ // Block of properties used only for debugging to record a SkPicture and save it in a file.
+ /**
+ * mCapturedFile is used to enforce we don't capture more than once for a given name (cause
+ * permissions don't allow to reset a property from render thread).
+ */
+ std::string mCapturedFile;
+ /**
+ * mCaptureSequence counts how many frames are left to take in the sequence.
+ */
+ int mCaptureSequence = 0;
+ /**
+ * mSavePictureProcessor is used to run the file saving code in a separate thread.
+ */
+ class SavePictureProcessor;
+ sp<SavePictureProcessor> mSavePictureProcessor;
+ /**
+ * mRecorder holds the current picture recorder. We could store it on the stack to support
+ * parallel tryCapture calls (not really needed).
+ */
+ std::unique_ptr<SkPictureRecorder> mRecorder;
+
static float mLightRadius;
static uint8_t mAmbientShadowAlpha;
static uint8_t mSpotShadowAlpha;
diff --git a/libs/hwui/tests/scripts/skp-capture.sh b/libs/hwui/tests/scripts/skp-capture.sh
new file mode 100755
index 0000000..54fa229
--- /dev/null
+++ b/libs/hwui/tests/scripts/skp-capture.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if [ -z "$1" ]; then
+ printf 'Usage:\n skp-capture.sh PACKAGE_NAME OPTIONAL_FRAME_COUNT\n\n'
+ printf "Use \`adb shell 'pm list packages'\` to get a listing.\n\n"
+ exit 1
+fi
+if ! command -v adb > /dev/null 2>&1; then
+ if [ -x "${ANDROID_SDK_ROOT}/platform-tools/adb" ]; then
+ adb() {
+ "${ANDROID_SDK_ROOT}/platform-tools/adb" "$@"
+ }
+ else
+ echo 'adb missing'
+ exit 2
+ fi
+fi
+phase1_timeout_seconds=15
+phase2_timeout_seconds=60
+package="$1"
+filename="$(date '+%H%M%S').skp"
+remote_path="/data/data/${package}/cache/${filename}"
+local_path_prefix="$(date '+%Y-%m-%d_%H%M%S')_${package}"
+local_path="${local_path_prefix}.skp"
+enable_capture_key='debug.hwui.capture_skp_enabled'
+enable_capture_value=$(adb shell "getprop '${enable_capture_key}'")
+#printf 'captureflag=' "$enable_capture_value" '\n'
+if [ -z "$enable_capture_value" ]; then
+ printf 'Capture SKP property need to be enabled first. Please use\n'
+ printf "\"adb shell setprop debug.hwui.capture_skp_enabled true\" and then restart\n"
+ printf "the process.\n\n"
+ exit 1
+fi
+if [ ! -z "$2" ]; then
+ adb shell "setprop 'debug.hwui.capture_skp_frames' $2"
+fi
+filename_key='debug.hwui.skp_filename'
+adb shell "setprop '${filename_key}' '${remote_path}'"
+spin() {
+ case "$spin" in
+ 1) printf '\b|';;
+ 2) printf '\b\\';;
+ 3) printf '\b-';;
+ *) printf '\b/';;
+ esac
+ spin=$(( ( ${spin:-0} + 1 ) % 4 ))
+ sleep $1
+}
+
+banner() {
+ printf '\n=====================\n'
+ printf ' %s' "$*"
+ printf '\n=====================\n'
+}
+banner '...WAITING...'
+adb_test_exist() {
+ test '0' = "$(adb shell "test -e \"$1\"; echo \$?")";
+}
+timeout=$(( $(date +%s) + $phase1_timeout_seconds))
+while ! adb_test_exist "$remote_path"; do
+ spin 0.05
+ if [ $(date +%s) -gt $timeout ] ; then
+ printf '\bTimed out.\n'
+ adb shell "setprop '${filename_key}' ''"
+ exit 3
+ fi
+done
+printf '\b'
+
+#read -n1 -r -p "Press any key to continue..." key
+
+banner '...SAVING...'
+adb_test_file_nonzero() {
+ # grab first byte of `du` output
+ X="$(adb shell "du \"$1\" 2> /dev/null | dd bs=1 count=1 2> /dev/null")"
+ test "$X" && test "$X" -ne 0
+}
+#adb_filesize() {
+# adb shell "wc -c \"$1\"" 2> /dev/null | awk '{print $1}'
+#}
+timeout=$(( $(date +%s) + $phase2_timeout_seconds))
+while ! adb_test_file_nonzero "$remote_path"; do
+ spin 0.05
+ if [ $(date +%s) -gt $timeout ] ; then
+ printf '\bTimed out.\n'
+ adb shell "setprop '${filename_key}' ''"
+ exit 3
+ fi
+done
+printf '\b'
+
+adb shell "setprop '${filename_key}' ''"
+
+i=0; while [ $i -lt 10 ]; do spin 0.10; i=$(($i + 1)); done; echo
+
+adb pull "$remote_path" "$local_path"
+if ! [ -f "$local_path" ] ; then
+ printf "something went wrong with `adb pull`."
+ exit 4
+fi
+adb shell rm "$remote_path"
+printf '\nSKP saved to file:\n %s\n\n' "$local_path"
+if [ ! -z "$2" ]; then
+ bridge="_"
+ adb shell "setprop 'debug.hwui.capture_skp_frames' ''"
+ for i in $(seq 2 $2); do
+ adb pull "${remote_path}_${i}" "${local_path_prefix}_${i}.skp"
+ adb shell rm "${remote_path}_${i}"
+ done
+fi
+
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 4c3e182..d182d78 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -22,6 +22,7 @@
#include "IContextFactory.h"
#include "pipeline/skia/SkiaDisplayList.h"
#include "pipeline/skia/SkiaPipeline.h"
+#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaRecordingCanvas.h"
#include "renderthread/CanvasContext.h"
#include "tests/common/TestUtils.h"
@@ -335,7 +336,7 @@
EXPECT_EQ(3, canvas.getIndex());
}
-RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
/* R is backward projected on B and C is a layer.
A
/ \
@@ -450,7 +451,8 @@
LayerUpdateQueue layerUpdateQueue;
layerUpdateQueue.enqueueLayerWithDamage(child.get(),
android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
- SkiaPipeline::renderLayersImpl(layerUpdateQueue, true, false);
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ pipeline->renderLayersImpl(layerUpdateQueue, true, false);
EXPECT_EQ(1, drawCounter); //assert index 0 is drawn on the layer
RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
diff --git a/libs/services/Android.mk b/libs/services/Android.mk
index cbfd4b3..d72059a 100644
--- a/libs/services/Android.mk
+++ b/libs/services/Android.mk
@@ -21,7 +21,8 @@
LOCAL_MODULE := libservices
LOCAL_SRC_FILES := \
../../core/java/com/android/internal/os/IDropBoxManagerService.aidl \
- src/os/DropBoxManager.cpp
+ src/os/DropBoxManager.cpp \
+ src/os/StatsLogEventWrapper.cpp
LOCAL_AIDL_INCLUDES := \
$(LOCAL_PATH)/../../core/java
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
new file mode 100644
index 0000000..255619c
--- /dev/null
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -0,0 +1,47 @@
+/*
+ * 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 STATS_LOG_EVENT_WRAPPER_H
+#define STATS_LOG_EVENT_WRAPPER_H
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/Status.h>
+#include <utils/RefBase.h>
+#include <vector>
+
+namespace android {
+namespace os {
+
+// Represents a parcelable object. Only used to send data from Android OS to statsd.
+class StatsLogEventWrapper : public android::Parcelable {
+ public:
+ StatsLogEventWrapper();
+
+ StatsLogEventWrapper(StatsLogEventWrapper&& in) = default;
+
+ android::status_t writeToParcel(android::Parcel* out) const;
+
+ android::status_t readFromParcel(const android::Parcel* in);
+
+ // These are public for ease of conversion.
+ std::vector<uint8_t> bytes;
+};
+} // Namespace os
+} // Namespace android
+
+
+#endif // STATS_LOG_EVENT_WRAPPER_H
+
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
new file mode 100644
index 0000000..8b3aa9a
--- /dev/null
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 <android/os/StatsLogEventWrapper.h>
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/Status.h>
+#include <utils/RefBase.h>
+#include <vector>
+
+using android::Parcel;
+using android::Parcelable;
+using android::status_t;
+using std::vector;
+
+namespace android {
+namespace os {
+
+StatsLogEventWrapper::StatsLogEventWrapper(){};
+
+status_t StatsLogEventWrapper::writeToParcel(Parcel* out) const {
+ out->writeByteVector(bytes);
+ return ::android::NO_ERROR;
+};
+
+status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) {
+ in->readByteVector(&bytes);
+ return ::android::NO_ERROR;
+};
+
+} // Namespace os
+} // Namespace android
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index 9f2c08e..aa0d0cc 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -20,8 +20,10 @@
import android.annotation.Nullable;
import android.annotation.SystemService;
import android.app.Activity;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.media.projection.IMediaProjection;
import android.os.Handler;
import android.os.IBinder;
@@ -71,8 +73,11 @@
*/
public Intent createScreenCaptureIntent() {
Intent i = new Intent();
- i.setClassName("com.android.systemui",
- "com.android.systemui.media.MediaProjectionPermissionActivity");
+ final ComponentName mediaProjectionPermissionDialogComponent =
+ ComponentName.unflattenFromString(mContext.getResources().getString(
+ com.android.internal.R.string
+ .config_mediaProjectionPermissionDialogComponent));
+ i.setComponent(mediaProjectionPermissionDialogComponent);
return i;
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 51afada..76e7782 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -21,9 +21,12 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.IApplicationThread;
+import android.app.IServiceConnection;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManagerInternal;
@@ -99,7 +102,6 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.widget.IRemoteViewsAdapterConnection;
import com.android.internal.widget.IRemoteViewsFactory;
import com.android.server.LocalServices;
import com.android.server.WidgetBackupProvider;
@@ -191,10 +193,6 @@
}
};
- // Manages active connections to RemoteViewsServices.
- private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
- mBoundRemoteViewsServices = new HashMap<>();
-
// Manages persistent references to RemoteViewsServices from different App Widgets.
private final HashMap<Pair<Integer, FilterComparison>, HashSet<Integer>>
mRemoteViewsServicesAppWidgets = new HashMap<>();
@@ -1209,17 +1207,14 @@
}
@Override
- public void bindRemoteViewsService(String callingPackage, int appWidgetId,
- Intent intent, IBinder callbacks) {
+ public boolean bindRemoteViewsService(String callingPackage, int appWidgetId, Intent intent,
+ IApplicationThread caller, IBinder activtiyToken, IServiceConnection connection,
+ int flags) {
final int userId = UserHandle.getCallingUserId();
-
if (DEBUG) {
Slog.i(TAG, "bindRemoteViewsService() " + userId);
}
- // Make sure the package runs under the caller uid.
- mSecurityPolicy.enforceCallFromPackage(callingPackage);
-
synchronized (mLock) {
ensureGroupStateLoadedLocked(userId);
@@ -1254,76 +1249,35 @@
mSecurityPolicy.enforceServiceExistsAndRequiresBindRemoteViewsPermission(
componentName, widget.provider.getUserId());
- // Good to go - the service pakcage is correct, it exists for the correct
+ // Good to go - the service package is correct, it exists for the correct
// user, and requires the bind permission.
- // If there is already a connection made for this service intent, then
- // disconnect from that first. (This does not allow multiple connections
- // to the same service under the same key).
- ServiceConnectionProxy connection = null;
- FilterComparison fc = new FilterComparison(intent);
- Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ // Ask ActivityManager to bind it. Notice that we are binding the service with the
+ // caller app instead of DevicePolicyManagerService.
+ if(ActivityManager.getService().bindService(
+ caller, activtiyToken, intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ connection, flags, mContext.getOpPackageName(),
+ widget.provider.getUserId()) != 0) {
- if (mBoundRemoteViewsServices.containsKey(key)) {
- connection = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
- connection.disconnect();
- unbindService(connection);
- mBoundRemoteViewsServices.remove(key);
- }
-
- // Bind to the RemoteViewsService (which will trigger a callback to the
- // RemoteViewsAdapter.onServiceConnected())
- connection = new ServiceConnectionProxy(callbacks);
- bindService(intent, connection, widget.provider.info.getProfile());
- mBoundRemoteViewsServices.put(key, connection);
-
- // Add it to the mapping of RemoteViewsService to appWidgetIds so that we
- // can determine when we can call back to the RemoteViewsService later to
- // destroy associated factories.
- Pair<Integer, FilterComparison> serviceId = Pair.create(widget.provider.id.uid, fc);
- incrementAppWidgetServiceRefCount(appWidgetId, serviceId);
- }
- }
-
- @Override
- public void unbindRemoteViewsService(String callingPackage, int appWidgetId, Intent intent) {
- final int userId = UserHandle.getCallingUserId();
-
- if (DEBUG) {
- Slog.i(TAG, "unbindRemoteViewsService() " + userId);
- }
-
- // Make sure the package runs under the caller uid.
- mSecurityPolicy.enforceCallFromPackage(callingPackage);
-
- synchronized (mLock) {
- ensureGroupStateLoadedLocked(userId);
-
- // Unbind from the RemoteViewsService (which will trigger a callback to the bound
- // RemoteViewsAdapter)
- Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
- new FilterComparison(intent));
- if (mBoundRemoteViewsServices.containsKey(key)) {
- // We don't need to use the appWidgetId until after we are sure there is something
- // to unbind. Note that this may mask certain issues with apps calling unbind()
- // more than necessary.
-
- // NOTE: The lookup is enforcing security across users by making
- // sure the caller can only access widgets it hosts or provides.
- Widget widget = lookupWidgetLocked(appWidgetId,
- Binder.getCallingUid(), callingPackage);
-
- if (widget == null) {
- throw new IllegalArgumentException("Bad widget id " + appWidgetId);
+ // Add it to the mapping of RemoteViewsService to appWidgetIds so that we
+ // can determine when we can call back to the RemoteViewsService later to
+ // destroy associated factories.
+ incrementAppWidgetServiceRefCount(appWidgetId,
+ Pair.create(widget.provider.id.uid, new FilterComparison(intent)));
+ return true;
}
-
- ServiceConnectionProxy connection = (ServiceConnectionProxy)
- mBoundRemoteViewsServices.get(key);
- connection.disconnect();
- mContext.unbindService(connection);
- mBoundRemoteViewsServices.remove(key);
+ } catch (RemoteException ex) {
+ // Same process, should not happen.
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
}
}
+
+ // Failed to bind.
+ return false;
}
@Override
@@ -1754,7 +1708,9 @@
private void deleteAppWidgetLocked(Widget widget) {
// We first unbind all services that are bound to this id
- unbindAppWidgetRemoteViewsServicesLocked(widget);
+ // Check if we need to destroy any services (if no other app widgets are
+ // referencing the same service)
+ decrementAppWidgetServiceRefCount(widget);
Host host = widget.host;
host.widgets.remove(widget);
@@ -1796,28 +1752,6 @@
}
}
- // Unbinds from a RemoteViewsService when we delete an app widget
- private void unbindAppWidgetRemoteViewsServicesLocked(Widget widget) {
- int appWidgetId = widget.appWidgetId;
- // Unbind all connections to Services bound to this AppWidgetId
- Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
- .iterator();
- while (it.hasNext()) {
- final Pair<Integer, Intent.FilterComparison> key = it.next();
- if (key.first == appWidgetId) {
- final ServiceConnectionProxy conn = (ServiceConnectionProxy)
- mBoundRemoteViewsServices.get(key);
- conn.disconnect();
- mContext.unbindService(conn);
- it.remove();
- }
- }
-
- // Check if we need to destroy any services (if no other app widgets are
- // referencing the same service)
- decrementAppWidgetServiceRefCount(widget);
- }
-
// Destroys the cached factory on the RemoteViewsService's side related to the specified intent
private void destroyRemoteViewsService(final Intent intent, Widget widget) {
final ServiceConnection conn = new ServiceConnection() {
@@ -1853,7 +1787,7 @@
// Adds to the ref-count for a given RemoteViewsService intent
private void incrementAppWidgetServiceRefCount(int appWidgetId,
Pair<Integer, FilterComparison> serviceId) {
- HashSet<Integer> appWidgetIds = null;
+ final HashSet<Integer> appWidgetIds;
if (mRemoteViewsServicesAppWidgets.containsKey(serviceId)) {
appWidgetIds = mRemoteViewsServicesAppWidgets.get(serviceId);
} else {
@@ -4055,40 +3989,6 @@
}
}
- /**
- * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
- * needs to be a static inner class since a reference to the ServiceConnection is held globally
- * and may lead us to leak AppWidgetService instances (if there were more than one).
- */
- private static final class ServiceConnectionProxy implements ServiceConnection {
- private final IRemoteViewsAdapterConnection mConnectionCb;
-
- ServiceConnectionProxy(IBinder connectionCb) {
- mConnectionCb = IRemoteViewsAdapterConnection.Stub
- .asInterface(connectionCb);
- }
-
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- mConnectionCb.onServiceConnected(service);
- } catch (RemoteException re) {
- Slog.e(TAG, "Error passing service interface", re);
- }
- }
-
- public void onServiceDisconnected(ComponentName name) {
- disconnect();
- }
-
- public void disconnect() {
- try {
- mConnectionCb.onServiceDisconnected();
- } catch (RemoteException re) {
- Slog.e(TAG, "Error clearing service interface", re);
- }
- }
- }
-
private class LoadedWidgetState {
final Widget widget;
final int hostTag;
@@ -4642,7 +4542,9 @@
// reconstructed due to the restore
host.widgets.remove(widget);
provider.widgets.remove(widget);
- unbindAppWidgetRemoteViewsServicesLocked(widget);
+ // Check if we need to destroy any services (if no other app widgets are
+ // referencing the same service)
+ decrementAppWidgetServiceRefCount(widget);
removeWidgetLocked(widget);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 59fc34d..b720f74 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -846,12 +846,9 @@
* when necessary.
*/
public void logContextCommittedLocked() {
- if (mResponses == null) {
- if (sVerbose) Slog.v(TAG, "logContextCommittedLocked(): skipped (no responses)");
- return;
- }
+ final FillResponse lastResponse = getLastResponseLocked("logContextCommited()");
+ if (lastResponse == null) return;
- final FillResponse lastResponse = mResponses.valueAt(mResponses.size() -1);
final int flags = lastResponse.getFlags();
if ((flags & FillResponse.FLAG_TRACK_CONTEXT_COMMITED) == 0) {
if (sDebug) Slog.d(TAG, "logContextCommittedLocked(): ignored by flags " + flags);
@@ -1599,11 +1596,10 @@
* Checks whether a view should be ignored.
*/
private boolean isIgnoredLocked(AutofillId id) {
- if (mResponses == null || mResponses.size() == 0) {
- return false;
- }
// Always check the latest response only
- final FillResponse response = mResponses.valueAt(mResponses.size() - 1);
+ final FillResponse response = getLastResponseLocked(null);
+ if (response == null) return false;
+
return ArrayUtils.contains(response.getIgnoredIds(), id);
}
@@ -1680,13 +1676,10 @@
}
private void updateTrackedIdsLocked() {
- if (mResponses == null || mResponses.size() == 0) {
- return;
- }
-
// Only track the views of the last response as only those are reported back to the
// service, see #showSaveLocked
- final FillResponse response = mResponses.valueAt(getLastResponseIndexLocked());
+ final FillResponse response = getLastResponseLocked(null);
+ if (response == null) return;
ArraySet<AutofillId> trackedViews = null;
boolean saveOnAllViewsInvisible = false;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 736a766..c15b5e2 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3005,12 +3005,7 @@
@Override
public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) {
- if (wasTrimmed) {
- // Task was trimmed from the recent tasks list -- remove the active task record as well
- // since the user won't really be able to go back to it
- removeTaskByIdLocked(task.taskId, false /* killProcess */,
- false /* removeFromRecents */);
- }
+ // TODO: Trim active task once b/68045330 is fixed
task.removedFromRecents();
}
diff --git a/services/core/java/com/android/server/am/LockTaskNotify.java b/services/core/java/com/android/server/am/LockTaskNotify.java
index 5d6e9b5..1dcb0ad 100644
--- a/services/core/java/com/android/server/am/LockTaskNotify.java
+++ b/services/core/java/com/android/server/am/LockTaskNotify.java
@@ -17,8 +17,6 @@
package com.android.server.am;
import android.content.Context;
-import android.os.Handler;
-import android.os.Message;
import android.os.SystemClock;
import android.util.Slog;
import android.view.WindowManager;
@@ -27,21 +25,19 @@
import com.android.internal.R;
/**
- * Helper to manage showing/hiding a image to notify them that they are entering
- * or exiting screen pinning mode.
+ * Helper to manage showing/hiding a image to notify them that they are entering or exiting screen
+ * pinning mode. All exposed methods should be called from a handler thread.
*/
public class LockTaskNotify {
private static final String TAG = "LockTaskNotify";
private static final long SHOW_TOAST_MINIMUM_INTERVAL = 1000;
private final Context mContext;
- private final H mHandler;
private Toast mLastToast;
private long mLastShowToastTime;
public LockTaskNotify(Context context) {
mContext = context;
- mHandler = new H();
}
/** Show "Screen pinned" toast. */
@@ -56,10 +52,6 @@
/** Show a toast that describes the gesture the user should use to escape pinned mode. */
void showEscapeToast() {
- mHandler.obtainMessage(H.SHOW_ESCAPE_TOAST).sendToTarget();
- }
-
- private void handleShowEscapeToast() {
long showToastTime = SystemClock.elapsedRealtime();
if ((showToastTime - mLastShowToastTime) < SHOW_TOAST_MINIMUM_INTERVAL) {
Slog.i(TAG, "Ignore toast since it is requested in very short interval.");
@@ -79,17 +71,4 @@
toast.show();
return toast;
}
-
- private final class H extends Handler {
- private static final int SHOW_ESCAPE_TOAST = 3;
-
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case SHOW_ESCAPE_TOAST:
- handleShowEscapeToast();
- break;
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 515fa39..755c5f0 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -34,8 +34,6 @@
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioAttributes;
-import android.nfc.INfcAdapter;
-import android.nfc.NfcAdapter;
import android.os.FileUtils;
import android.os.Handler;
import android.os.PowerManager;
@@ -124,7 +122,6 @@
private static String METRIC_RADIOS = "shutdown_radios";
private static String METRIC_BT = "shutdown_bt";
private static String METRIC_RADIO = "shutdown_radio";
- private static String METRIC_NFC = "shutdown_nfc";
private static String METRIC_SM = "shutdown_storage_manager";
private final Object mActionDoneSync = new Object();
@@ -629,29 +626,14 @@
Thread t = new Thread() {
public void run() {
TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog();
- boolean nfcOff;
boolean bluetoothReadyForShutdown;
boolean radioOff;
- final INfcAdapter nfc =
- INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
final ITelephony phone =
ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
final IBluetoothManager bluetooth =
IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
- try {
- nfcOff = nfc == null ||
- nfc.getState() == NfcAdapter.STATE_OFF;
- if (!nfcOff) {
- Log.w(TAG, "Turning off NFC...");
- metricStarted(METRIC_NFC);
- nfc.disable(false); // Don't persist new state
- }
- } catch (RemoteException ex) {
- Log.e(TAG, "RemoteException during NFC shutdown", ex);
- nfcOff = true;
- }
try {
bluetoothReadyForShutdown = bluetooth == null ||
@@ -678,7 +660,7 @@
radioOff = true;
}
- Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
+ Log.i(TAG, "Waiting for Bluetooth and Radio...");
long delay = endTime - SystemClock.elapsedRealtime();
while (delay > 0) {
@@ -722,23 +704,9 @@
.logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO));
}
}
- if (!nfcOff) {
- try {
- nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
- } catch (RemoteException ex) {
- Log.e(TAG, "RemoteException during NFC shutdown", ex);
- nfcOff = true;
- }
- if (nfcOff) {
- Log.i(TAG, "NFC turned off.");
- metricEnded(METRIC_NFC);
- shutdownTimingsTraceLog
- .logDuration("ShutdownNfc", TRON_METRICS.get(METRIC_NFC));
- }
- }
- if (radioOff && bluetoothReadyForShutdown && nfcOff) {
- Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
+ if (radioOff && bluetoothReadyForShutdown) {
+ Log.i(TAG, "Radio and Bluetooth shutdown complete.");
done[0] = true;
break;
}
@@ -755,7 +723,7 @@
} catch (InterruptedException ex) {
}
if (!done[0]) {
- Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
+ Log.w(TAG, "Timed out waiting for Radio and Bluetooth shutdown.");
}
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index ca3dd05..22d2bcf 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -33,12 +33,14 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.StatsLogEventWrapper;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
import java.util.ArrayList;
import java.util.List;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.KernelWakelockReader;
import com.android.internal.os.KernelWakelockStats;
@@ -49,6 +51,7 @@
/**
* Helper service for statsd (the native stats management service in cmds/statsd/).
* Used for registering and receiving alarms on behalf of statsd.
+ *
* @hide
*/
public class StatsCompanionService extends IStatsCompanionService.Stub {
@@ -90,7 +93,7 @@
// Needed since the new user basically has a version of every app.
informAllUidsLocked(context);
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
+ Slog.e(TAG, "Failed to inform statsd latest update of all apps", e);
forgetEverything();
}
}
@@ -99,9 +102,9 @@
Slog.w(TAG, "Registered receiver for ACTION_PACKAGE_REPLACE AND ADDED.");
}
- private final static int[] toIntArray(List<Integer> list){
+ private final static int[] toIntArray(List<Integer> list) {
int[] ret = new int[list.size()];
- for(int i = 0;i < ret.length;i++) {
+ for (int i = 0; i < ret.length; i++) {
ret[i] = list.get(i);
}
return ret;
@@ -113,7 +116,7 @@
PackageManager pm = context.getPackageManager();
final List<UserInfo> users = um.getUsers(true);
if (DEBUG) {
- Slog.w(TAG, "Iterating over "+users.size() + " profiles.");
+ Slog.w(TAG, "Iterating over " + users.size() + " profiles.");
}
List<Integer> uids = new ArrayList();
@@ -122,23 +125,23 @@
// Add in all the apps for every user/profile.
for (UserInfo profile : users) {
- List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
- for (int j = 0; j < pi.size(); j++) {
- if (pi.get(j).applicationInfo != null) {
- uids.add(pi.get(j).applicationInfo.uid);
- versions.add(pi.get(j).versionCode);
- apps.add(pi.get(j).packageName);
- }
- }
+ List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
+ for (int j = 0; j < pi.size(); j++) {
+ if (pi.get(j).applicationInfo != null) {
+ uids.add(pi.get(j).applicationInfo.uid);
+ versions.add(pi.get(j).versionCode);
+ apps.add(pi.get(j).packageName);
+ }
+ }
}
sStatsd.informAllUidData(toIntArray(uids), toIntArray(versions), apps.toArray(new
- String[apps.size()]));
+ String[apps.size()]));
if (DEBUG) {
- Slog.w(TAG, "Sent data for "+uids.size() +" apps");
+ Slog.w(TAG, "Sent data for " + uids.size() + " apps");
}
}
- public final static class AppUpdateReceiver extends BroadcastReceiver {
+ public final static class AppUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
@@ -147,7 +150,7 @@
* waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
*/
if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED) &&
- intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
return; // Keep only replacing or normal add and remove.
}
synchronized (sStatsdLock) {
@@ -180,9 +183,9 @@
}
}
}
- };
+ }
- public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
+ public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
@@ -200,7 +203,7 @@
}
// AlarmManager releases its own wakelock here.
}
- };
+ }
public final static class PollingAlarmReceiver extends BroadcastReceiver {
@Override
@@ -220,7 +223,7 @@
}
// AlarmManager releases its own wakelock here.
}
- };
+ }
@Override // Binder call
public void setAnomalyAlarm(long timestampMs) {
@@ -286,33 +289,34 @@
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
@Override // Binder call
- public String pullData(int pullCode) {
+ public StatsLogEventWrapper[] pullData(int pullCode) {
enforceCallingPermission();
- if (DEBUG) Slog.d(TAG, "Fetching " + pullCode);
+ if (DEBUG) {
+ Slog.d(TAG, "Pulling " + pullCode);
+ }
- StringBuilder s = new StringBuilder(); // TODO: use and return a Parcel instead of a string
+ List<StatsLogEventWrapper> ret = new ArrayList<>();
switch (pullCode) {
- case PULL_CODE_KERNEL_WAKELOCKS:
+ case PULL_CODE_KERNEL_WAKELOCKS: {
final KernelWakelockStats wakelockStats =
mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
-
for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
String name = ent.getKey();
KernelWakelockStats.Entry kws = ent.getValue();
- s.append("Wakelock ")
- .append(name)
- .append(", time=")
- .append(kws.mTotalTime)
- .append(", count=")
- .append(kws.mCount)
- .append('\n');
+ StatsLogEventWrapper e = new StatsLogEventWrapper(101, 4);
+ e.writeInt(kws.mCount);
+ e.writeInt(kws.mVersion);
+ e.writeLong(kws.mTotalTime);
+ e.writeString(name);
+ ret.add(e);
}
break;
+ }
default:
Slog.w(TAG, "No such pollable data as " + pullCode);
return null;
}
- return s.toString();
+ return ret.toArray(new StatsLogEventWrapper[ret.size()]);
}
@Override // Binder call
@@ -331,7 +335,9 @@
// Lifecycle and related code
- /** Fetches the statsd IBinder service */
+ /**
+ * Fetches the statsd IBinder service
+ */
private static IStatsManager fetchStatsdService() {
return IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
}
@@ -363,13 +369,17 @@
}
}
- /** Now that the android system is ready, StatsCompanion is ready too, so inform statsd. */
+ /**
+ * Now that the android system is ready, StatsCompanion is ready too, so inform statsd.
+ */
private void systemReady() {
if (DEBUG) Slog.d(TAG, "Learned that systemReady");
sayHiToStatsd();
}
- /** Tells statsd that statscompanion is ready. If the binder call returns, link to statsd. */
+ /**
+ * Tells statsd that statscompanion is ready. If the binder call returns, link to statsd.
+ */
private void sayHiToStatsd() {
synchronized (sStatsdLock) {
if (sStatsd != null) {
@@ -398,14 +408,14 @@
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null,
- null);
+ null);
// Setup receiver for user initialize (which happens once for a new user) and
// if a user is removed.
filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
filter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiverAsUser(mUserUpdateReceiver, UserHandle.ALL,
- filter, null, null);
+ filter, null, null);
// Pull the latest state of UID->app name, version mapping when statsd starts.
informAllUidsLocked(mContext);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c59f44e..80f6a4b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -175,6 +175,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
@@ -1682,6 +1683,10 @@
return getCallingUid() == Process.myUid();
}
+ void binderWithCleanCallingIdentity(@NonNull ThrowingRunnable action) {
+ Binder.withCleanCallingIdentity(action);
+ }
+
final int userHandleGetCallingUserId() {
return UserHandle.getUserId(binderGetCallingUid());
}
@@ -9023,6 +9028,31 @@
}
@Override
+ public boolean setTime(ComponentName who, long millis) {
+ Preconditions.checkNotNull(who, "ComponentName is null in setTime");
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ // Don't allow set time when auto time is on.
+ if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
+ return false;
+ }
+ mInjector.binderWithCleanCallingIdentity(() -> mInjector.getAlarmManager().setTime(millis));
+ return true;
+ }
+
+ @Override
+ public boolean setTimeZone(ComponentName who, String timeZone) {
+ Preconditions.checkNotNull(who, "ComponentName is null in setTimeZone");
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ // Don't allow set timezone when auto timezone is on.
+ if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
+ return false;
+ }
+ mInjector.binderWithCleanCallingIdentity(() ->
+ mInjector.getAlarmManager().setTimeZone(timeZone));
+ return true;
+ }
+
+ @Override
public void setSecureSetting(ComponentName who, String setting, String value) {
Preconditions.checkNotNull(who, "ComponentName is null");
int callingUserId = mInjector.userHandleGetCallingUserId();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b78fcdd..d9db22e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1551,11 +1551,6 @@
traceEnd();
}
- // Statsd helper
- traceBeginAndSlog("StartStatsCompanionService");
- mSystemServiceManager.startService(StatsCompanionService.Lifecycle.class);
- traceEnd();
-
// Before things start rolling, be sure we have decided whether
// we are in safe mode.
final boolean safeMode = wm.detectSafeMode();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 5471715..ae4b569 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -15,6 +15,7 @@
*/
package com.android.server.devicepolicy;
+import android.app.AlarmManager;
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -34,11 +35,13 @@
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.security.KeyChain;
+import android.support.annotation.NonNull;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Pair;
import android.view.IWindowManager;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.widget.LockPatternUtils;
import java.io.File;
@@ -194,6 +197,9 @@
}
@Override
+ AlarmManager getAlarmManager() {return services.alarmManager;}
+
+ @Override
LockPatternUtils newLockPatternUtils() {
return services.lockPatternUtils;
}
@@ -234,6 +240,11 @@
}
@Override
+ void binderWithCleanCallingIdentity(@NonNull ThrowingRunnable action) {
+ context.binder.withCleanCallingIdentity(action);
+ }
+
+ @Override
int binderGetCallingUid() {
return context.binder.getCallingUid();
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index a8bf8f1..e1e9cf5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3090,6 +3090,47 @@
assertEquals(-1, dpm.getLastSecurityLogRetrievalTime());
}
+ public void testSetTime() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ dpm.setTime(admin1, 0);
+ verify(getServices().alarmManager).setTime(0);
+ }
+
+ public void testSetTimeFailWithPO() throws Exception {
+ setupProfileOwner();
+ assertExpectException(SecurityException.class, null, () -> dpm.setTime(admin1, 0));
+ }
+
+ public void testSetTimeWithAutoTimeOn() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ when(getServices().settings.settingsGlobalGetInt(Settings.Global.AUTO_TIME, 0))
+ .thenReturn(1);
+ assertFalse(dpm.setTime(admin1, 0));
+ }
+
+ public void testSetTimeZone() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ dpm.setTimeZone(admin1, "Asia/Shanghai");
+ verify(getServices().alarmManager).setTimeZone("Asia/Shanghai");
+ }
+
+ public void testSetTimeZoneFailWithPO() throws Exception {
+ setupProfileOwner();
+ assertExpectException(SecurityException.class, null,
+ () -> dpm.setTimeZone(admin1, "Asia/Shanghai"));
+ }
+
+ public void testSetTimeZoneWithAutoTimeZoneOn() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ when(getServices().settings.settingsGlobalGetInt(Settings.Global.AUTO_TIME_ZONE, 0))
+ .thenReturn(1);
+ assertFalse(dpm.setTimeZone(admin1, "Asia/Shanghai"));
+ }
+
public void testGetLastBugReportRequestTime() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 9702118..7e11e87 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -30,8 +30,12 @@
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManagerInternal;
+import android.support.annotation.NonNull;
import android.test.mock.MockContext;
import android.util.ArrayMap;
+import android.util.ExceptionUtils;
+
+import com.android.internal.util.FunctionalUtils;
import org.junit.Assert;
@@ -95,6 +99,21 @@
callingPid = (int) token;
}
+ public void withCleanCallingIdentity(@NonNull FunctionalUtils.ThrowingRunnable action) {
+ long callingIdentity = clearCallingIdentity();
+ Throwable throwableToPropagate = null;
+ try {
+ action.run();
+ } catch (Throwable throwable) {
+ throwableToPropagate = throwable;
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ if (throwableToPropagate != null) {
+ throw ExceptionUtils.propagate(throwableToPropagate);
+ }
+ }
+ }
+
public int getCallingUid() {
return callingUid;
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 56a3fb0..b8e8946 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -89,6 +89,9 @@
public void setUp() throws Exception {
if (!sOneTimeSetupDone) {
sOneTimeSetupDone = true;
+
+ // Allows to mock package local classes and methods
+ System.setProperty("dexmaker.share_classloader", "true");
MockitoAnnotations.initMocks(this);
}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index c3b2c48..9ccdd56 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -118,14 +118,8 @@
}
final int uid = Binder.getCallingUid();
- callback.asBinder().linkToDeath(new DeathRecipient() {
- @Override
- public void binderDied() {
- onAppCallbackDied(uid, subscriptionId);
- }
- }, 0);
- return initialize(subscriptionId, new MbmsDownloadSessionCallback() {
+ int result = initialize(subscriptionId, new MbmsDownloadSessionCallback() {
@Override
public void onError(int errorCode, String message) {
try {
@@ -153,6 +147,17 @@
}
}
});
+
+ if (result == MbmsErrors.SUCCESS) {
+ callback.asBinder().linkToDeath(new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }, 0);
+ }
+
+ return result;
}
/**
@@ -251,17 +256,6 @@
throw new NullPointerException("Callback must not be null");
}
- DeathRecipient deathRecipient = new DeathRecipient() {
- @Override
- public void binderDied() {
- onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
- mDownloadCallbackBinderMap.remove(callback.asBinder());
- mDownloadCallbackDeathRecipients.remove(callback.asBinder());
- }
- };
- mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
- callback.asBinder().linkToDeath(deathRecipient, 0);
-
DownloadStateCallback exposedCallback = new FilteredDownloadStateCallback(callback, flags) {
@Override
protected void onRemoteException(RemoteException e) {
@@ -269,9 +263,23 @@
}
};
- mDownloadCallbackBinderMap.put(callback.asBinder(), exposedCallback);
+ int result = registerStateCallback(downloadRequest, exposedCallback);
- return registerStateCallback(downloadRequest, exposedCallback);
+ if (result == MbmsErrors.SUCCESS) {
+ DeathRecipient deathRecipient = new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+ mDownloadCallbackBinderMap.remove(callback.asBinder());
+ mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+ }
+ };
+ mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
+ callback.asBinder().linkToDeath(deathRecipient, 0);
+ mDownloadCallbackBinderMap.put(callback.asBinder(), exposedCallback);
+ }
+
+ return result;
}
/**
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index 65b726d..a238153 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -70,14 +70,8 @@
}
final int uid = Binder.getCallingUid();
- callback.asBinder().linkToDeath(new DeathRecipient() {
- @Override
- public void binderDied() {
- onAppCallbackDied(uid, subscriptionId);
- }
- }, 0);
- return initialize(new MbmsStreamingSessionCallback() {
+ int result = initialize(new MbmsStreamingSessionCallback() {
@Override
public void onError(final int errorCode, final String message) {
try {
@@ -105,6 +99,17 @@
}
}
}, subscriptionId);
+
+ if (result == MbmsErrors.SUCCESS) {
+ callback.asBinder().linkToDeath(new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }, 0);
+ }
+
+ return result;
}
@@ -161,14 +166,8 @@
}
final int uid = Binder.getCallingUid();
- callback.asBinder().linkToDeath(new DeathRecipient() {
- @Override
- public void binderDied() {
- onAppCallbackDied(uid, subscriptionId);
- }
- }, 0);
- return startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() {
+ int result = startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() {
@Override
public void onError(final int errorCode, final String message) {
try {
@@ -215,6 +214,17 @@
}
}
});
+
+ if (result == MbmsErrors.SUCCESS) {
+ callback.asBinder().linkToDeath(new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }, 0);
+ }
+
+ return result;
}
/**
diff --git a/tools/locked_region_code_injection/Android.mk b/tools/locked_region_code_injection/Android.mk
index bb5f4d6..3f65151 100644
--- a/tools/locked_region_code_injection/Android.mk
+++ b/tools/locked_region_code_injection/Android.mk
@@ -6,10 +6,10 @@
LOCAL_MODULE := lockedregioncodeinjection
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_STATIC_JAVA_LIBRARIES := \
- asm-6.0_BETA \
- asm-commons-6.0_BETA \
- asm-tree-6.0_BETA \
- asm-analysis-6.0_BETA \
+ asm-6.0 \
+ asm-commons-6.0 \
+ asm-tree-6.0 \
+ asm-analysis-6.0 \
guava-21.0 \
include $(BUILD_HOST_JAVA_LIBRARY)