Merge "Import translations. DO NOT MERGE" into pi-dev
diff --git a/api/test-current.txt b/api/test-current.txt
index 2ef2025..f7bfeae 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -204,6 +204,10 @@
package android.app.usage {
+ public class NetworkStatsManager {
+ method public void setPollForce(boolean);
+ }
+
public class StorageStatsManager {
method public boolean isQuotaSupported(java.util.UUID);
method public boolean isReservedSupported(java.util.UUID);
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 2bb7edc..09343f1 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -147,9 +147,21 @@
}
public void runSetForceAdoptable() throws RemoteException {
- final boolean forceAdoptable = Boolean.parseBoolean(nextArg());
- mSm.setDebugFlags(forceAdoptable ? StorageManager.DEBUG_FORCE_ADOPTABLE : 0,
- StorageManager.DEBUG_FORCE_ADOPTABLE);
+ final int mask = StorageManager.DEBUG_ADOPTABLE_FORCE_ON
+ | StorageManager.DEBUG_ADOPTABLE_FORCE_OFF;
+ switch (nextArg()) {
+ case "on":
+ case "true":
+ mSm.setDebugFlags(StorageManager.DEBUG_ADOPTABLE_FORCE_ON, mask);
+ break;
+ case "off":
+ mSm.setDebugFlags(StorageManager.DEBUG_ADOPTABLE_FORCE_OFF, mask);
+ break;
+ case "default":
+ case "false":
+ mSm.setDebugFlags(0, mask);
+ break;
+ }
}
public void runSetSdcardfs() throws RemoteException {
@@ -289,7 +301,7 @@
System.err.println(" sm list-volumes [public|private|emulated|all]");
System.err.println(" sm has-adoptable");
System.err.println(" sm get-primary-storage-uuid");
- System.err.println(" sm set-force-adoptable [true|false]");
+ System.err.println(" sm set-force-adoptable [on|off|default]");
System.err.println(" sm set-virtual-disk [true|false]");
System.err.println("");
System.err.println(" sm partition DISK [public|private|mixed] [ratio]");
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index e0222d9..b085a09 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -66,7 +66,8 @@
src/subscriber/IncidentdReporter.cpp \
src/subscriber/SubscriberReporter.cpp \
src/HashableDimensionKey.cpp \
- src/guardrail/StatsdStats.cpp
+ src/guardrail/StatsdStats.cpp \
+ src/socket/StatsSocketListener.cpp
statsd_common_c_includes := \
$(LOCAL_PATH)/src \
@@ -96,7 +97,10 @@
android.hardware.health@2.0 \
android.hardware.power@1.0 \
android.hardware.power@1.1 \
- android.hardware.thermal@1.0
+ android.hardware.thermal@1.0 \
+ libpackagelistparser \
+ libsysutils \
+ libcutils
# =========
# statsd
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 8ce9ec7..e8904c6 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -14,10 +14,12 @@
* limitations under the License.
*/
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "StatsService.h"
#include "logd/LogReader.h"
+#include "socket/StatsSocketListener.h"
#include <binder/IInterface.h>
#include <binder/IPCThreadState.h>
@@ -35,7 +37,9 @@
using namespace android;
using namespace android::os::statsd;
-// ================================================================================
+const bool kUseLogd = false;
+const bool kUseStatsdSocket = true;
+
/**
* Thread function data.
*/
@@ -48,12 +52,8 @@
*/
static void* log_reader_thread_func(void* cookie) {
log_reader_thread_data* data = static_cast<log_reader_thread_data*>(cookie);
-
sp<LogReader> reader = new LogReader(data->service);
- // Tell StatsService that we're ready to go.
- data->service->Startup();
-
// Run the read loop. Never returns.
reader->Run();
@@ -96,10 +96,7 @@
return NO_ERROR;
}
-// ================================================================================
int main(int /*argc*/, char** /*argv*/) {
- status_t err;
-
// Set up the looper
sp<Looper> looper(Looper::prepare(0 /* opts */));
@@ -118,10 +115,25 @@
}
service->sayHiToStatsCompanion();
- // Start the log reader thread
- err = start_log_reader_thread(service);
- if (err != NO_ERROR) {
- return 1;
+ service->Startup();
+
+ sp<StatsSocketListener> socketListener = new StatsSocketListener(service);
+
+ if (kUseLogd) {
+ ALOGI("using logd");
+ // Start the log reader thread
+ status_t err = start_log_reader_thread(service);
+ if (err != NO_ERROR) {
+ return 1;
+ }
+ }
+
+ if (kUseStatsdSocket) {
+ ALOGI("using statsd socket");
+ // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
+ if (socketListener->startListener(600)) {
+ exit(1);
+ }
}
// Loop forever -- the reports run on this thread in a handler, and the
diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp
new file mode 100755
index 0000000..0392d67
--- /dev/null
+++ b/cmds/statsd/src/socket/StatsSocketListener.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <sys/cdefs.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+#include <unordered_map>
+
+#include "StatsSocketListener.h"
+#include "guardrail/StatsdStats.h"
+#include "stats_log_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static const int kLogMsgHeaderSize = 28;
+
+StatsSocketListener::StatsSocketListener(const sp<LogListener>& listener)
+ : SocketListener(getLogSocket(), false /*start listen*/), mListener(listener) {
+}
+
+StatsSocketListener::~StatsSocketListener() {
+}
+
+bool StatsSocketListener::onDataAvailable(SocketClient* cli) {
+ static bool name_set;
+ if (!name_set) {
+ prctl(PR_SET_NAME, "statsd.writer");
+ name_set = true;
+ }
+
+ // + 1 to ensure null terminator if MAX_PAYLOAD buffer is received
+ char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) + LOGGER_ENTRY_MAX_PAYLOAD +
+ 1];
+ struct iovec iov = {buffer, sizeof(buffer) - 1};
+
+ alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
+ struct msghdr hdr = {
+ NULL, 0, &iov, 1, control, sizeof(control), 0,
+ };
+
+ int socket = cli->getSocket();
+
+ // To clear the entire buffer is secure/safe, but this contributes to 1.68%
+ // overhead under logging load. We are safe because we check counts, but
+ // still need to clear null terminator
+ // memset(buffer, 0, sizeof(buffer));
+ ssize_t n = recvmsg(socket, &hdr, 0);
+ if (n <= (ssize_t)(sizeof(android_log_header_t))) {
+ return false;
+ }
+
+ buffer[n] = 0;
+
+ struct ucred* cred = NULL;
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+ while (cmsg != NULL) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
+ cred = (struct ucred*)CMSG_DATA(cmsg);
+ break;
+ }
+ cmsg = CMSG_NXTHDR(&hdr, cmsg);
+ }
+
+ struct ucred fake_cred;
+ if (cred == NULL) {
+ cred = &fake_cred;
+ cred->pid = 0;
+ cred->uid = DEFAULT_OVERFLOWUID;
+ }
+
+ char* ptr = ((char*)buffer) + sizeof(android_log_header_t);
+ n -= sizeof(android_log_header_t);
+
+ log_msg msg;
+
+ msg.entry.len = n;
+ msg.entry.hdr_size = kLogMsgHeaderSize;
+ msg.entry.sec = time(nullptr);
+ msg.entry.pid = cred->pid;
+ msg.entry.uid = cred->uid;
+
+ memcpy(msg.buf + kLogMsgHeaderSize, ptr, n + 1);
+ LogEvent event(msg);
+
+ // Call the listener
+ mListener->OnLogEvent(&event, false /*reconnected, N/A in statsd socket*/);
+
+ return true;
+}
+
+int StatsSocketListener::getLogSocket() {
+ static const char socketName[] = "statsdw";
+ int sock = android_get_control_socket(socketName);
+
+ if (sock < 0) { // statsd started up in init.sh
+ sock = socket_local_server(socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM);
+
+ int on = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
+ return -1;
+ }
+ }
+ return sock;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/socket/StatsSocketListener.h b/cmds/statsd/src/socket/StatsSocketListener.h
new file mode 100644
index 0000000..73e4d33
--- /dev/null
+++ b/cmds/statsd/src/socket/StatsSocketListener.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <sysutils/SocketListener.h>
+#include <utils/RefBase.h>
+#include "logd/LogListener.h"
+
+// DEFAULT_OVERFLOWUID is defined in linux/highuid.h, which is not part of
+// the uapi headers for userspace to use. This value is filled in on the
+// out-of-band socket credentials if the OS fails to find one available.
+// One of the causes of this is if SO_PASSCRED is set, all the packets before
+// that point will have this value. We also use it in a fake credential if
+// no socket credentials are supplied.
+#ifndef DEFAULT_OVERFLOWUID
+#define DEFAULT_OVERFLOWUID 65534
+#endif
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StatsSocketListener : public SocketListener, public virtual android::RefBase {
+public:
+ StatsSocketListener(const sp<LogListener>& listener);
+
+ virtual ~StatsSocketListener();
+
+protected:
+ virtual bool onDataAvailable(SocketClient* cli);
+
+private:
+ static int getLogSocket();
+ /**
+ * Who is going to get the events when they're read.
+ */
+ sp<LogListener> mListener;
+};
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc
index 3a0c224..f349292 100644
--- a/cmds/statsd/statsd.rc
+++ b/cmds/statsd/statsd.rc
@@ -14,6 +14,7 @@
service statsd /system/bin/statsd
class main
+ socket statsdw dgram+passcred 0222 statsd statsd
user statsd
group statsd log
writepid /dev/cpuset/system-background/tasks
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 20149de..b456b72 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -759,6 +759,7 @@
private static final int LOG_AM_ON_STOP_CALLED = 30049;
private static final int LOG_AM_ON_RESTART_CALLED = 30058;
private static final int LOG_AM_ON_DESTROY_CALLED = 30060;
+ private static final int LOG_AM_ON_ACTIVITY_RESULT_CALLED = 30062;
private static class ManagedDialog {
Dialog mDialog;
@@ -7438,8 +7439,8 @@
}
}
- void dispatchActivityResult(String who, int requestCode,
- int resultCode, Intent data) {
+ void dispatchActivityResult(String who, int requestCode, int resultCode, Intent data,
+ String reason) {
if (false) Log.v(
TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
+ ", resCode=" + resultCode + ", data=" + data);
@@ -7475,6 +7476,7 @@
frag.onActivityResult(requestCode, resultCode, data);
}
}
+ writeEventLog(LOG_AM_ON_ACTIVITY_RESULT_CALLED, reason);
}
/**
diff --git a/core/java/android/app/ActivityGroup.java b/core/java/android/app/ActivityGroup.java
index 78a4dfd..228067c 100644
--- a/core/java/android/app/ActivityGroup.java
+++ b/core/java/android/app/ActivityGroup.java
@@ -16,11 +16,11 @@
package android.app;
-import java.util.HashMap;
-
import android.content.Intent;
import android.os.Bundle;
+import java.util.HashMap;
+
/**
* A screen that contains and runs multiple embedded activities.
*
@@ -109,7 +109,7 @@
@Override
void dispatchActivityResult(String who, int requestCode, int resultCode,
- Intent data) {
+ Intent data, String reason) {
if (who != null) {
Activity act = mLocalActivityManager.getActivity(who);
/*
@@ -123,7 +123,7 @@
return;
}
}
- super.dispatchActivityResult(who, requestCode, resultCode, data);
+ super.dispatchActivityResult(who, requestCode, resultCode, data, reason);
}
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a41da0e..037a87b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3730,7 +3730,7 @@
r.pendingIntents = null;
}
if (r.pendingResults != null) {
- deliverResults(r, r.pendingResults);
+ deliverResults(r, r.pendingResults, reason);
r.pendingResults = null;
}
r.activity.performResume(r.startsNotResumed, reason);
@@ -4299,7 +4299,7 @@
WindowManagerGlobal.getInstance().reportNewConfiguration(mConfiguration);
}
- private void deliverResults(ActivityClientRecord r, List<ResultInfo> results) {
+ private void deliverResults(ActivityClientRecord r, List<ResultInfo> results, String reason) {
final int N = results.size();
for (int i=0; i<N; i++) {
ResultInfo ri = results.get(i);
@@ -4311,7 +4311,7 @@
if (DEBUG_RESULTS) Slog.v(TAG,
"Delivering result to activity " + r + " : " + ri);
r.activity.dispatchActivityResult(ri.mResultWho,
- ri.mRequestCode, ri.mResultCode, ri.mData);
+ ri.mRequestCode, ri.mResultCode, ri.mData, reason);
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
@@ -4324,7 +4324,7 @@
}
@Override
- public void handleSendResult(IBinder token, List<ResultInfo> results) {
+ public void handleSendResult(IBinder token, List<ResultInfo> results, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (DEBUG_RESULTS) Slog.v(TAG, "Handling send result to " + r);
if (r != null) {
@@ -4359,9 +4359,9 @@
}
}
checkAndBlockForNetworkAccess();
- deliverResults(r, results);
+ deliverResults(r, results, reason);
if (resumed) {
- r.activity.performResume(false, "handleSendResult");
+ r.activity.performResume(false, reason);
r.activity.mTemporaryPause = false;
}
}
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index e26d989..ea0d703 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -121,7 +121,7 @@
Configuration overrideConfig, int displayId);
/** Deliver result from another activity. */
- public abstract void handleSendResult(IBinder token, List<ResultInfo> results);
+ public abstract void handleSendResult(IBinder token, List<ResultInfo> results, String reason);
/** Deliver multi-window mode change notification. */
public abstract void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode,
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 919f714..569c2bd 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -575,6 +575,11 @@
void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds,
in Rect tempDockedTaskInsetBounds,
in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
+ /**
+ * Sets whether we are currently in an interactive split screen resize operation where we
+ * are changing the docked stack size.
+ */
+ void setSplitScreenResizing(boolean resizing);
int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
// Gets the URI permissions granted to an arbitrary package (or all packages if null)
// NOTE: this is different from getPersistedUriPermissions(), which returns the URIs the package
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
index 545463c..e57f585 100644
--- a/core/java/android/app/servertransaction/ActivityResultItem.java
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -16,7 +16,6 @@
package android.app.servertransaction;
-import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.app.ClientTransactionHandler;
@@ -37,16 +36,17 @@
private List<ResultInfo> mResultInfoList;
+ /* TODO(b/78294732)
@Override
public int getPostExecutionState() {
return ON_RESUME;
- }
+ }*/
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
- client.handleSendResult(token, mResultInfoList);
+ client.handleSendResult(token, mResultInfoList, "ACTIVITY_RESULT");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 0b21196..9f46f20 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.app.usage.NetworkStats.Bucket;
import android.content.Context;
import android.net.ConnectivityManager;
@@ -111,7 +112,9 @@
/** @hide */
public static final int FLAG_POLL_ON_OPEN = 1 << 0;
/** @hide */
- public static final int FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN = 1 << 1;
+ public static final int FLAG_POLL_FORCE = 1 << 1;
+ /** @hide */
+ public static final int FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN = 1 << 2;
private int mFlags;
@@ -141,6 +144,16 @@
}
/** @hide */
+ @TestApi
+ public void setPollForce(boolean pollForce) {
+ if (pollForce) {
+ mFlags |= FLAG_POLL_FORCE;
+ } else {
+ mFlags &= ~FLAG_POLL_FORCE;
+ }
+ }
+
+ /** @hide */
public void setAugmentWithSubscriptionPlan(boolean augmentWithSubscriptionPlan) {
if (augmentWithSubscriptionPlan) {
mFlags |= FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN;
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 435bcb0..90d407c 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -498,7 +498,7 @@
@Override
public int hashCode() {
// secondaryIds and vendorIds are ignored for equality/hashing
- return Objects.hash(mProgramType, mPrimaryId);
+ return mPrimaryId.hashCode();
}
@Override
@@ -507,7 +507,8 @@
if (!(obj instanceof ProgramSelector)) return false;
ProgramSelector other = (ProgramSelector) obj;
// secondaryIds and vendorIds are ignored for equality/hashing
- return other.getProgramType() == mProgramType && mPrimaryId.equals(other.getPrimaryId());
+ // programType can be inferred from primaryId, thus not checked
+ return mPrimaryId.equals(other.getPrimaryId());
}
private ProgramSelector(Parcel in) {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8905ad1..2d1bb2f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -115,7 +115,7 @@
/** {@hide} */
public static final String PROP_HAS_RESERVED = "vold.has_reserved";
/** {@hide} */
- public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
+ public static final String PROP_ADOPTABLE = "persist.sys.adoptable";
/** {@hide} */
public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe";
/** {@hide} */
@@ -197,15 +197,17 @@
public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
/** {@hide} */
- public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
+ public static final int DEBUG_ADOPTABLE_FORCE_ON = 1 << 0;
/** {@hide} */
- public static final int DEBUG_EMULATE_FBE = 1 << 1;
+ public static final int DEBUG_ADOPTABLE_FORCE_OFF = 1 << 1;
/** {@hide} */
- public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2;
+ public static final int DEBUG_EMULATE_FBE = 1 << 2;
/** {@hide} */
- public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3;
+ public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 3;
/** {@hide} */
- public static final int DEBUG_VIRTUAL_DISK = 1 << 4;
+ public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 4;
+ /** {@hide} */
+ public static final int DEBUG_VIRTUAL_DISK = 1 << 5;
// NOTE: keep in sync with installd
/** {@hide} */
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 32737c5..a7d70d0 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -807,7 +807,8 @@
* @return An array of active notifications, sorted in natural order.
*/
public StatusBarNotification[] getActiveNotifications() {
- return getActiveNotifications(null, TRIM_FULL);
+ StatusBarNotification[] activeNotifications = getActiveNotifications(null, TRIM_FULL);
+ return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
}
/**
@@ -842,7 +843,8 @@
*/
@SystemApi
public StatusBarNotification[] getActiveNotifications(int trim) {
- return getActiveNotifications(null, trim);
+ StatusBarNotification[] activeNotifications = getActiveNotifications(null, trim);
+ return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
}
/**
@@ -858,7 +860,8 @@
* same order as the key list.
*/
public StatusBarNotification[] getActiveNotifications(String[] keys) {
- return getActiveNotifications(keys, TRIM_FULL);
+ StatusBarNotification[] activeNotifications = getActiveNotifications(keys, TRIM_FULL);
+ return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
}
/**
@@ -890,6 +893,9 @@
private StatusBarNotification[] cleanUpNotificationList(
ParceledListSlice<StatusBarNotification> parceledList) {
+ if (parceledList == null || parceledList.getList() == null) {
+ return new StatusBarNotification[0];
+ }
List<StatusBarNotification> list = parceledList.getList();
ArrayList<StatusBarNotification> corruptNotifications = null;
int N = list.size();
diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java
index e60377b..549f8b3 100644
--- a/core/java/android/text/method/LinkMovementMethod.java
+++ b/core/java/android/text/method/LinkMovementMethod.java
@@ -25,6 +25,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.textclassifier.TextLinks.TextLinkSpan;
import android.widget.TextView;
/**
@@ -130,64 +131,70 @@
selStart = selEnd = -1;
switch (what) {
- case CLICK:
- if (selStart == selEnd) {
- return false;
- }
+ case CLICK:
+ if (selStart == selEnd) {
+ return false;
+ }
- ClickableSpan[] link = buffer.getSpans(selStart, selEnd, ClickableSpan.class);
+ ClickableSpan[] links = buffer.getSpans(selStart, selEnd, ClickableSpan.class);
- if (link.length != 1)
- return false;
+ if (links.length != 1) {
+ return false;
+ }
- link[0].onClick(widget);
- break;
+ ClickableSpan link = links[0];
+ if (link instanceof TextLinkSpan) {
+ ((TextLinkSpan) link).onClick(widget, TextLinkSpan.INVOCATION_METHOD_KEYBOARD);
+ } else {
+ link.onClick(widget);
+ }
+ break;
- case UP:
- int bestStart, bestEnd;
+ case UP:
+ int bestStart, bestEnd;
- bestStart = -1;
- bestEnd = -1;
+ bestStart = -1;
+ bestEnd = -1;
- for (int i = 0; i < candidates.length; i++) {
- int end = buffer.getSpanEnd(candidates[i]);
+ for (int i = 0; i < candidates.length; i++) {
+ int end = buffer.getSpanEnd(candidates[i]);
- if (end < selEnd || selStart == selEnd) {
- if (end > bestEnd) {
- bestStart = buffer.getSpanStart(candidates[i]);
- bestEnd = end;
+ if (end < selEnd || selStart == selEnd) {
+ if (end > bestEnd) {
+ bestStart = buffer.getSpanStart(candidates[i]);
+ bestEnd = end;
+ }
}
}
- }
- if (bestStart >= 0) {
- Selection.setSelection(buffer, bestEnd, bestStart);
- return true;
- }
+ if (bestStart >= 0) {
+ Selection.setSelection(buffer, bestEnd, bestStart);
+ return true;
+ }
- break;
+ break;
- case DOWN:
- bestStart = Integer.MAX_VALUE;
- bestEnd = Integer.MAX_VALUE;
+ case DOWN:
+ bestStart = Integer.MAX_VALUE;
+ bestEnd = Integer.MAX_VALUE;
- for (int i = 0; i < candidates.length; i++) {
- int start = buffer.getSpanStart(candidates[i]);
+ for (int i = 0; i < candidates.length; i++) {
+ int start = buffer.getSpanStart(candidates[i]);
- if (start > selStart || selStart == selEnd) {
- if (start < bestStart) {
- bestStart = start;
- bestEnd = buffer.getSpanEnd(candidates[i]);
+ if (start > selStart || selStart == selEnd) {
+ if (start < bestStart) {
+ bestStart = start;
+ bestEnd = buffer.getSpanEnd(candidates[i]);
+ }
}
}
- }
- if (bestEnd < Integer.MAX_VALUE) {
- Selection.setSelection(buffer, bestStart, bestEnd);
- return true;
- }
+ if (bestEnd < Integer.MAX_VALUE) {
+ Selection.setSelection(buffer, bestStart, bestEnd);
+ return true;
+ }
- break;
+ break;
}
return false;
@@ -215,8 +222,14 @@
ClickableSpan[] links = buffer.getSpans(off, off, ClickableSpan.class);
if (links.length != 0) {
+ ClickableSpan link = links[0];
if (action == MotionEvent.ACTION_UP) {
- links[0].onClick(widget);
+ if (link instanceof TextLinkSpan) {
+ ((TextLinkSpan) link).onClick(
+ widget, TextLinkSpan.INVOCATION_METHOD_TOUCH);
+ } else {
+ link.onClick(widget);
+ }
} else if (action == MotionEvent.ACTION_DOWN) {
if (widget.getContext().getApplicationInfo().targetSdkVersion
>= Build.VERSION_CODES.P) {
@@ -225,8 +238,8 @@
widget.hideFloatingToolbar(HIDE_FLOATING_TOOLBAR_DELAY_MS);
}
Selection.setSelection(buffer,
- buffer.getSpanStart(links[0]),
- buffer.getSpanEnd(links[0]));
+ buffer.getSpanStart(link),
+ buffer.getSpanEnd(link));
}
return true;
} else {
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 6486230..8395681 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -342,12 +342,6 @@
int getDockedStackSide();
/**
- * Sets whether we are currently in a drag resize operation where we are changing the docked
- * stack size.
- */
- void setDockedStackResizing(boolean resizing);
-
- /**
* Sets the region the user can touch the divider. This region will be excluded from the region
* which is used to cause a focus switch when dispatching touch.
*/
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 851b2c9..e7faf14 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -503,6 +503,22 @@
*/
public static class TextLinkSpan extends ClickableSpan {
+ /**
+ * How the clickspan is triggered.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({INVOCATION_METHOD_UNSPECIFIED, INVOCATION_METHOD_TOUCH,
+ INVOCATION_METHOD_KEYBOARD})
+ public @interface InvocationMethod {}
+
+ /** @hide */
+ public static final int INVOCATION_METHOD_UNSPECIFIED = -1;
+ /** @hide */
+ public static final int INVOCATION_METHOD_TOUCH = 0;
+ /** @hide */
+ public static final int INVOCATION_METHOD_KEYBOARD = 1;
+
private final TextLink mTextLink;
public TextLinkSpan(@NonNull TextLink textLink) {
@@ -511,16 +527,24 @@
@Override
public void onClick(View widget) {
+ onClick(widget, INVOCATION_METHOD_UNSPECIFIED);
+ }
+
+ /** @hide */
+ public final void onClick(View widget, @InvocationMethod int invocationMethod) {
if (widget instanceof TextView) {
final TextView textView = (TextView) widget;
final Context context = textView.getContext();
if (TextClassificationManager.getSettings(context).isSmartLinkifyEnabled()) {
- if (textView.requestFocus()) {
- textView.requestActionMode(this);
- } else {
- // If textView can not take focus, then simply handle the click as it will
- // be difficult to get rid of the floating action mode.
- textView.handleClick(this);
+ switch (invocationMethod) {
+ case INVOCATION_METHOD_TOUCH:
+ textView.requestActionMode(this);
+ break;
+ case INVOCATION_METHOD_KEYBOARD:// fall though
+ case INVOCATION_METHOD_UNSPECIFIED: // fall through
+ default:
+ textView.handleClick(this);
+ break;
}
} else {
if (mTextLink.mUrlSpan != null) {
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 2ce5a0b..63c2e96 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1176,6 +1176,9 @@
final boolean showIcon = isFirstItem && menuItem.getItemId() == R.id.textAssist;
final View menuItemButton = createMenuItemButton(
mContext, menuItem, mIconTextSpacing, showIcon);
+ if (!showIcon && menuItemButton instanceof LinearLayout) {
+ ((LinearLayout) menuItemButton).setGravity(Gravity.CENTER);
+ }
// Adding additional start padding for the first button to even out button spacing.
if (isFirstItem) {
@@ -1200,57 +1203,21 @@
final int menuItemButtonWidth = Math.min(
menuItemButton.getMeasuredWidth(), toolbarWidth);
- final boolean isNewGroup = !isFirstItem && lastGroupId != menuItem.getGroupId();
- final int extraPadding = isNewGroup ? menuItemButton.getPaddingEnd() * 2 : 0;
-
// Check if we can fit an item while reserving space for the overflowButton.
final boolean canFitWithOverflow =
menuItemButtonWidth <=
- availableWidth - mOverflowButtonSize.getWidth() - extraPadding;
+ availableWidth - mOverflowButtonSize.getWidth();
final boolean canFitNoOverflow =
- isLastItem && menuItemButtonWidth <= availableWidth - extraPadding;
+ isLastItem && menuItemButtonWidth <= availableWidth;
if (canFitWithOverflow || canFitNoOverflow) {
- if (isNewGroup) {
- final View divider = createDivider(mContext);
- final int dividerWidth = divider.getLayoutParams().width;
-
- // Add extra padding to the end of the previous button.
- // Half of the extra padding (less borderWidth) goes to the previous button.
- final View previousButton = mMainPanel.getChildAt(
- mMainPanel.getChildCount() - 1);
- final int prevPaddingEnd = previousButton.getPaddingEnd()
- + extraPadding / 2 - dividerWidth;
- previousButton.setPaddingRelative(
- previousButton.getPaddingStart(),
- previousButton.getPaddingTop(),
- prevPaddingEnd,
- previousButton.getPaddingBottom());
- final ViewGroup.LayoutParams prevParams = previousButton.getLayoutParams();
- prevParams.width += extraPadding / 2 - dividerWidth;
- previousButton.setLayoutParams(prevParams);
-
- // Add extra padding to the start of this button.
- // Other half of the extra padding goes to this button.
- final int paddingStart = menuItemButton.getPaddingStart()
- + extraPadding / 2;
- menuItemButton.setPaddingRelative(
- paddingStart,
- menuItemButton.getPaddingTop(),
- menuItemButton.getPaddingEnd(),
- menuItemButton.getPaddingBottom());
-
- // Include a divider.
- mMainPanel.addView(divider);
- }
-
setButtonTagAndClickListener(menuItemButton, menuItem);
// Set tooltips for main panel items, but not overflow items (b/35726766).
menuItemButton.setTooltipText(menuItem.getTooltipText());
mMainPanel.addView(menuItemButton);
final ViewGroup.LayoutParams params = menuItemButton.getLayoutParams();
- params.width = menuItemButtonWidth + extraPadding / 2;
+ params.width = menuItemButtonWidth;
menuItemButton.setLayoutParams(params);
- availableWidth -= menuItemButtonWidth + extraPadding;
+ availableWidth -= menuItemButtonWidth;
remainingMenuItems.pop();
} else {
break;
@@ -1726,30 +1693,6 @@
return popupWindow;
}
- private static View createDivider(Context context) {
- // TODO: Inflate this instead.
- View divider = new View(context);
-
- int _1dp = (int) TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, 1, context.getResources().getDisplayMetrics());
- LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
- _1dp, ViewGroup.LayoutParams.MATCH_PARENT);
- params.setMarginsRelative(0, _1dp * 10, 0, _1dp * 10);
- divider.setLayoutParams(params);
-
- TypedArray a = context.obtainStyledAttributes(
- new TypedValue().data, new int[] { R.attr.floatingToolbarDividerColor });
- divider.setBackgroundColor(a.getColor(0, 0));
- a.recycle();
-
- divider.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- divider.setEnabled(false);
- divider.setFocusable(false);
- divider.setContentDescription(null);
-
- return divider;
- }
-
/**
* Creates an "appear" animation for the specified view.
*
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 2aa40fd..a135b28 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -536,7 +536,7 @@
<dimen name="floating_toolbar_menu_image_width">24dp</dimen>
<dimen name="floating_toolbar_menu_image_button_width">56dp</dimen>
<dimen name="floating_toolbar_menu_image_button_vertical_padding">12dp</dimen>
- <dimen name="floating_toolbar_menu_button_side_padding">11dp</dimen>
+ <dimen name="floating_toolbar_menu_button_side_padding">8dp</dimen>
<dimen name="floating_toolbar_overflow_image_button_width">60dp</dimen>
<dimen name="floating_toolbar_overflow_side_padding">18dp</dimen>
<dimen name="floating_toolbar_text_size">14sp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0de2cb0..590f988 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1623,6 +1623,8 @@
<java-symbol type="anim" name="task_open_enter_cross_profile_apps" />
<java-symbol type="anim" name="activity_translucent_open_enter" />
<java-symbol type="anim" name="activity_translucent_close_exit" />
+ <java-symbol type="anim" name="activity_open_enter" />
+ <java-symbol type="anim" name="activity_close_exit" />
<java-symbol type="array" name="config_autoRotationTiltTolerance" />
<java-symbol type="array" name="config_keyboardTapVibePattern" />
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index 3eefc36..fe58116 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -40,7 +40,10 @@
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
+import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -50,7 +53,6 @@
import org.junit.runner.RunWith;
import org.mockito.InOrder;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -231,12 +233,12 @@
@Test
public void testActivityResultRequiredStateResolution() {
- ActivityResultItem activityResultItem = ActivityResultItem.obtain(new ArrayList<>());
+ PostExecItem postExecItem = new PostExecItem(ON_RESUME);
IBinder token = mock(IBinder.class);
ClientTransaction transaction = ClientTransaction.obtain(null /* client */,
token /* activityToken */);
- transaction.addCallback(activityResultItem);
+ transaction.addCallback(postExecItem);
// Verify resolution that should get to onPause
mClientRecord.setState(ON_RESUME);
@@ -395,4 +397,54 @@
return mExecutorHelper.getLifecyclePath(mClientRecord.getLifecycleState(), finish,
true /* excludeLastState */).toArray();
}
+
+ /** A transaction item that requires some specific post-execution state. */
+ private static class PostExecItem extends StubItem {
+
+ @LifecycleState
+ private int mPostExecutionState;
+
+ PostExecItem(@LifecycleState int state) {
+ mPostExecutionState = state;
+ }
+
+ @Override
+ public int getPostExecutionState() {
+ return mPostExecutionState;
+ }
+ }
+
+ /** Stub implementation of a transaction item that works as a base class for items in tests. */
+ private static class StubItem extends ClientTransactionItem {
+
+ private StubItem() {
+ }
+
+ private StubItem(Parcel in) {
+ }
+
+ @Override
+ public void execute(ClientTransactionHandler client, IBinder token,
+ PendingTransactionActions pendingActions) {
+ }
+
+ @Override
+ public void recycle() {
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+
+ public static final Parcelable.Creator<StubItem> CREATOR =
+ new Parcelable.Creator<StubItem>() {
+ public StubItem createFromParcel(Parcel in) {
+ return new StubItem(in);
+ }
+
+ public StubItem[] newArray(int size) {
+ return new StubItem[size];
+ }
+ };
+ }
}
diff --git a/core/tests/coretests/src/android/graphics/RectTest.java b/core/tests/coretests/src/android/graphics/RectTest.java
new file mode 100644
index 0000000..d31d7d5
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/RectTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import static android.graphics.Rect.copyOrNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class RectTest {
+
+ @Test
+ public void copyOrNull_passesThroughNull() {
+ assertNull(copyOrNull(null));
+ }
+
+ @Test
+ public void copyOrNull_copiesNonNull() {
+ final Rect orig = new Rect(1, 2, 3, 4);
+ final Rect copy = copyOrNull(orig);
+
+ assertEquals(orig, copy);
+ assertNotSame(orig, copy);
+ }
+}
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index aff942d..3843cb9 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -17,6 +17,7 @@
package android.graphics;
import android.annotation.CheckResult;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -99,6 +100,16 @@
}
}
+ /**
+ * Returns a copy of {@code r} if {@code r} is not {@code null}, or {@code null} otherwise.
+ *
+ * @hide
+ */
+ @Nullable
+ public static Rect copyOrNull(@Nullable Rect r) {
+ return r == null ? null : new Rect(r);
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 213fa8e..0f04b5d 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -146,8 +146,6 @@
name: "libhwui_defaults",
defaults: ["hwui_defaults"],
- shared_libs: ["libstatslog"],
-
whole_static_libs: ["libskia"],
srcs: [
@@ -336,7 +334,6 @@
],
shared_libs: [
"libmemunreachable",
- "libstatslog",
],
cflags: [
"-include debug/wrap_gles.h",
@@ -404,7 +401,6 @@
whole_static_libs: ["libhwui"],
shared_libs: [
"libmemunreachable",
- "libstatslog",
],
srcs: [
@@ -429,7 +425,6 @@
whole_static_libs: ["libhwui_static_debug"],
shared_libs: [
"libmemunreachable",
- "libstatslog",
],
srcs: [
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 81a7980..e6d2a6f 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -18,7 +18,6 @@
#include <errno.h>
#include <inttypes.h>
-#include <statslog.h>
#include <sys/mman.h>
#include <algorithm>
@@ -182,7 +181,6 @@
ALOGI("%s", ss.str().c_str());
// Just so we have something that counts up, the value is largely irrelevant
ATRACE_INT(ss.str().c_str(), ++sDaveyCount);
- android::util::stats_write(android::util::DAVEY_OCCURRED, getuid(), ns2ms(totalDuration));
}
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 6368607..50c9b5c 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -902,16 +902,18 @@
<!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
<string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
+ <!-- [CHAR_LIMIT=NONE] Label for battery on main page of settings -->
+ <string name="power_remaining_settings_home_page"><xliff:g id="percentage" example="10%">%1$s</xliff:g> - <xliff:g id="time_string" example="1 hour left based on your usage">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
- <string name="power_remaining_duration_only">About <xliff:g id="time">%1$s</xliff:g> left</string>
+ <string name="power_remaining_duration_only">About <xliff:g id="time_remaining">%1$s</xliff:g> left</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
- <string name="power_discharging_duration">About <xliff:g id="time">%1$s</xliff:g> left (<xliff:g id="level">%2$s</xliff:g>)</string>
+ <string name="power_discharging_duration">About <xliff:g id="time_remaining">%1$s</xliff:g> left (<xliff:g id="level">%2$s</xliff:g>)</string>
<!-- [CHAR_LIMIT=60] Label for estimated remaining duration of battery discharging -->
- <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time">%1$s</xliff:g> left based on your usage</string>
+ <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time_remaining">%1$s</xliff:g> left based on your usage</string>
<!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
- <string name="power_discharging_duration_enhanced">About <xliff:g id="time">%1$s</xliff:g> left based on your usage (<xliff:g id="level">%2$s</xliff:g>)</string>
+ <string name="power_discharging_duration_enhanced">About <xliff:g id="time_remaining">%1$s</xliff:g> left based on your usage (<xliff:g id="level">%2$s</xliff:g>)</string>
<!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
- <string name="power_remaining_duration_only_short"><xliff:g id="time">%1$s</xliff:g> left</string>
+ <string name="power_remaining_duration_only_short"><xliff:g id="time_remaining">%1$s</xliff:g> left</string>
<!-- [CHAR_LIMIT=100] Label for enhanced estimated time that phone will run out of battery -->
<string name="power_discharge_by_enhanced">Should last until about <xliff:g id="time">%1$s</xliff:g> based on your usage (<xliff:g id="level">%2$s</xliff:g>)</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
new file mode 100644
index 0000000..e92b36a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settingslib;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Process;
+import android.os.UserHandle;
+
+/**
+ * Utility class that allows Settings to use SystemUI to relay broadcasts related to pinned slices.
+ */
+public class SliceBroadcastRelay {
+
+ public static final String ACTION_REGISTER
+ = "com.android.settingslib.action.REGISTER_SLICE_RECEIVER";
+ public static final String ACTION_UNREGISTER
+ = "com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER";
+ public static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+
+ public static final String EXTRA_URI = "uri";
+ public static final String EXTRA_RECEIVER = "receiver";
+ public static final String EXTRA_FILTER = "filter";
+
+ public static void registerReceiver(Context context, Uri registerKey,
+ Class<? extends BroadcastReceiver> receiver, IntentFilter filter) {
+ Intent registerBroadcast = new Intent(ACTION_REGISTER);
+ registerBroadcast.setPackage(SYSTEMUI_PACKAGE);
+ registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey,
+ Process.myUserHandle().getIdentifier()));
+ registerBroadcast.putExtra(EXTRA_RECEIVER,
+ new ComponentName(context.getPackageName(), receiver.getName()));
+ registerBroadcast.putExtra(EXTRA_FILTER, filter);
+
+ context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM);
+ }
+
+ public static void unregisterReceivers(Context context, Uri registerKey) {
+ Intent registerBroadcast = new Intent(ACTION_UNREGISTER);
+ registerBroadcast.setPackage(SYSTEMUI_PACKAGE);
+ registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey,
+ Process.myUserHandle().getIdentifier()));
+
+ context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM);
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
index ad300f4..53f7e44 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
@@ -50,6 +50,4 @@
public abstract void onStateChanged(State state);
public abstract int getDetailY();
-
- public void setExpansion(float expansion) {}
}
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 74c22b0..49d142a 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -76,7 +76,7 @@
android:layout_below="@id/label_group"
android:clickable="false"
android:ellipsize="marquee"
- android:maxLines="1"
+ android:singleLine="true"
android:padding="0dp"
android:visibility="gone"
android:gravity="center"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f49d3de4..7eb08c4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -350,6 +350,7 @@
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.ScreenDecorations</item>
<item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
+ <item>com.android.systemui.SliceBroadcastRelayHandler</item>
</string-array>
<!-- SystemUI vender service, used in config_systemUIServiceComponents. -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index d22aab5..3ecf89c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -49,4 +49,9 @@
* Notifies SystemUI that Overview is shown.
*/
void onOverviewShown(boolean fromHome) = 6;
+
+ /**
+ * Get the secondary split screen app's rectangle when not minimized.
+ */
+ Rect getNonMinimizedSplitScreenSecondaryBounds() = 7;
}
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index e1540ea..282a8f1 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -22,7 +22,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Handler;
@@ -42,6 +41,7 @@
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.GraphicBufferCompat;
+import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -147,6 +147,19 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ public Rect getNonMinimizedSplitScreenSecondaryBounds() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ Divider divider = ((SystemUIApplication) mContext).getComponent(Divider.class);
+ if (divider != null) {
+ return divider.getView().getNonMinimizedSplitScreenSecondaryBounds();
+ }
+ return null;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
};
private final BroadcastReceiver mLauncherStateChangedReceiver = new BroadcastReceiver() {
diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
new file mode 100644
index 0000000..68f5836
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.SliceBroadcastRelay;
+
+/**
+ * Allows settings to register certain broadcasts to launch the settings app for pinned slices.
+ * @see SliceBroadcastRelay
+ */
+public class SliceBroadcastRelayHandler extends SystemUI {
+ private static final String TAG = "SliceBroadcastRelay";
+ private static final boolean DEBUG = false;
+
+ private final ArrayMap<Uri, BroadcastRelay> mRelays = new ArrayMap<>();
+
+ @Override
+ public void start() {
+ if (DEBUG) Log.d(TAG, "Start");
+ IntentFilter filter = new IntentFilter(SliceBroadcastRelay.ACTION_REGISTER);
+ filter.addAction(SliceBroadcastRelay.ACTION_UNREGISTER);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ @VisibleForTesting
+ void handleIntent(Intent intent) {
+ if (SliceBroadcastRelay.ACTION_REGISTER.equals(intent.getAction())) {
+ Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI);
+ ComponentName receiverClass =
+ intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_RECEIVER);
+ IntentFilter filter = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_FILTER);
+ if (DEBUG) Log.d(TAG, "Register " + uri + " " + receiverClass + " " + filter);
+ getOrCreateRelay(uri).register(mContext, receiverClass, filter);
+ } else if (SliceBroadcastRelay.ACTION_UNREGISTER.equals(intent.getAction())) {
+ Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI);
+ if (DEBUG) Log.d(TAG, "Unregister " + uri);
+ getAndRemoveRelay(uri).unregister(mContext);
+ }
+ }
+
+ private BroadcastRelay getOrCreateRelay(Uri uri) {
+ BroadcastRelay ret = mRelays.get(uri);
+ if (ret == null) {
+ ret = new BroadcastRelay(uri);
+ mRelays.put(uri, ret);
+ }
+ return ret;
+ }
+
+ private BroadcastRelay getAndRemoveRelay(Uri uri) {
+ return mRelays.remove(uri);
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ handleIntent(intent);
+ }
+ };
+
+ private static class BroadcastRelay extends BroadcastReceiver {
+
+ private final ArraySet<ComponentName> mReceivers = new ArraySet<>();
+ private final UserHandle mUserId;
+
+ public BroadcastRelay(Uri uri) {
+ mUserId = new UserHandle(ContentProvider.getUserIdFromUri(uri));
+ }
+
+ public void register(Context context, ComponentName receiver, IntentFilter filter) {
+ mReceivers.add(receiver);
+ context.registerReceiver(this, filter);
+ }
+
+ public void unregister(Context context) {
+ context.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ for (ComponentName receiver : mReceivers) {
+ intent.setComponent(receiver);
+ if (DEBUG) Log.d(TAG, "Forwarding " + receiver + " " + intent + " " + mUserId);
+ context.sendBroadcastAsUser(intent, mUserId);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index d8d07c0..1fd6023 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -56,6 +56,7 @@
private AnimatorSet mBounceAnimatorSet;
private int mAnimatingToPage = -1;
+ private float mLastExpansion;
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -172,8 +173,19 @@
@Override
public void setExpansion(float expansion) {
- for (TileRecord tr : mTiles) {
- tr.tileView.setExpansion(expansion);
+ mLastExpansion = expansion;
+ updateSelected();
+ }
+
+ private void updateSelected() {
+ // Start the marquee when fully expanded and stop when fully collapsed. Leave as is for
+ // other expansion ratios since there is no way way to pause the marquee.
+ if (mLastExpansion > 0f && mLastExpansion < 1f) {
+ return;
+ }
+ boolean selected = mLastExpansion == 1f;
+ for (int i = 0; i < mPages.size(); i++) {
+ mPages.get(i).setSelected(i == getCurrentItem() ? selected : false);
}
}
@@ -323,6 +335,7 @@
new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
+ updateSelected();
if (mPageIndicator == null) return;
if (mPageListener != null) {
mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index d21b06f..5649f7f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -107,15 +107,6 @@
}
@Override
- public void setExpansion(float expansion) {
- // Start the marquee when fully expanded and stop when fully collapsed. Leave as is for
- // other expansion ratios since there is no way way to pause the marquee.
- boolean selected = expansion == 1f ? true : expansion == 0f ? false : mLabel.isSelected();
- mLabel.setSelected(selected);
- mSecondLine.setSelected(selected);
- }
-
- @Override
protected void handleStateChanged(QSTile.State state) {
super.handleStateChanged(state);
if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 3d8e037..1149ad1 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -378,6 +378,23 @@
return mWindowManagerProxy;
}
+ public Rect getNonMinimizedSplitScreenSecondaryBounds() {
+ calculateBoundsForPosition(mSnapTargetBeforeMinimized.position,
+ DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect);
+ switch (mDockSide) {
+ case WindowManager.DOCKED_LEFT:
+ mOtherTaskRect.right -= mStableInsets.right;
+ break;
+ case WindowManager.DOCKED_RIGHT:
+ mOtherTaskRect.left -= mStableInsets.left;
+ break;
+ case WindowManager.DOCKED_TOP:
+ mOtherTaskRect.bottom -= mStableInsets.bottom;
+ break;
+ }
+ return mOtherTaskRect;
+ }
+
public boolean startDragging(boolean animate, boolean touching) {
cancelFlingAnimation();
if (touching) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 85a6062..1e5b37c 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -180,7 +180,7 @@
@Override
public void run() {
try {
- WindowManagerGlobal.getWindowManagerService().setDockedStackResizing(resizing);
+ ActivityManager.getService().setSplitScreenResizing(resizing);
} catch (RemoteException e) {
Log.w(TAG, "Error calling setDockedStackResizing: " + e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index dbd1cd4..f1e2302 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -20,7 +20,9 @@
import android.content.pm.UserInfo;
import android.content.res.ColorStateList;
import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.os.AsyncTask;
+import android.os.UserHandle;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -31,6 +33,7 @@
import androidx.car.widget.PagedListView;
+import com.android.internal.util.UserIcons;
import com.android.settingslib.users.UserManagerHelper;
import com.android.systemui.R;
@@ -180,13 +183,7 @@
@Override
public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
UserRecord userRecord = mUsers.get(position);
- if (!userRecord.mIsAddUser) {
- holder.mUserAvatarImageView.setImageBitmap(mUserManagerHelper
- .getUserIcon(userRecord.mInfo));
- } else {
- holder.mUserAvatarImageView.setImageDrawable(mContext
- .getDrawable(R.drawable.car_add_circle_round));
- }
+ holder.mUserAvatarImageView.setImageBitmap(getUserRecordIcon(userRecord));
holder.mUserNameTextView.setText(userRecord.mInfo.name);
holder.mView.setOnClickListener(v -> {
if (userRecord == null) {
@@ -219,6 +216,20 @@
}
+ private Bitmap getUserRecordIcon(UserRecord userRecord) {
+ if (userRecord.mIsStartGuestSession) {
+ return UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
+ mContext.getResources(), UserHandle.USER_NULL, false));
+ }
+
+ if (userRecord.mIsAddUser) {
+ return UserIcons.convertToBitmap(mContext
+ .getDrawable(R.drawable.car_add_circle_round));
+ }
+
+ return mUserManagerHelper.getUserIcon(userRecord.mInfo);
+ }
+
private class AddNewUserTask extends AsyncTask<String, Void, UserInfo> {
@Override
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 767a24a..1be8322 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -57,6 +57,12 @@
<service
android:name="com.android.systemui.qs.external.TileLifecycleManagerTests$FakeTileService"
android:process=":killable" />
+
+ <receiver android:name="com.android.systemui.SliceBroadcastRelayHandlerTest$Receiver">
+ <intent-filter>
+ <action android:name="com.android.systemui.action.TEST_ACTION" />
+ </intent-filter>
+ </receiver>
</application>
<instrumentation android:name="android.testing.TestableInstrumentation"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
new file mode 100644
index 0000000..4abac56
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.settingslib.SliceBroadcastRelay;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
+
+ private static final String TEST_ACTION = "com.android.systemui.action.TEST_ACTION";
+
+ @Test
+ public void testRegister() {
+ Uri testUri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority("something")
+ .path("test")
+ .build();
+ SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
+ relayHandler.mContext = spy(mContext);
+
+ Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
+ intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
+ new ComponentName(mContext.getPackageName(), Receiver.class.getName()));
+ IntentFilter value = new IntentFilter(TEST_ACTION);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
+
+ relayHandler.handleIntent(intent);
+ verify(relayHandler.mContext).registerReceiver(any(), eq(value));
+ }
+
+ @Test
+ public void testUnregister() {
+ Uri testUri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority("something")
+ .path("test")
+ .build();
+ SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
+ relayHandler.mContext = spy(mContext);
+
+ Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
+ intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
+ new ComponentName(mContext.getPackageName(), Receiver.class.getName()));
+ IntentFilter value = new IntentFilter(TEST_ACTION);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
+
+ relayHandler.handleIntent(intent);
+ ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value));
+
+ intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
+ relayHandler.handleIntent(intent);
+ verify(relayHandler.mContext).unregisterReceiver(eq(relay.getValue()));
+ }
+
+ @Test
+ public void testRelay() {
+ Receiver.sReceiver = mock(BroadcastReceiver.class);
+ Uri testUri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority("something")
+ .path("test")
+ .build();
+ SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
+ relayHandler.mContext = spy(mContext);
+
+ Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
+ intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
+ new ComponentName(mContext.getPackageName(), Receiver.class.getName()));
+ IntentFilter value = new IntentFilter(TEST_ACTION);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
+
+ relayHandler.handleIntent(intent);
+ ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value));
+ relay.getValue().onReceive(relayHandler.mContext, new Intent(TEST_ACTION));
+
+ verify(Receiver.sReceiver, timeout(2000)).onReceive(any(), any());
+ }
+
+ public static class Receiver extends BroadcastReceiver {
+ private static BroadcastReceiver sReceiver;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (sReceiver != null) sReceiver.onReceive(context, intent);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 99e0459..6c35bda 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -25,11 +25,9 @@
import static android.os.storage.OnObbStateChangeListener.MOUNTED;
import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
-import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.readStringAttribute;
-import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
@@ -249,7 +247,6 @@
private static final String TAG_VOLUMES = "volumes";
private static final String ATTR_VERSION = "version";
private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid";
- private static final String ATTR_FORCE_ADOPTABLE = "forceAdoptable";
private static final String TAG_VOLUME = "volume";
private static final String ATTR_TYPE = "type";
private static final String ATTR_FS_UUID = "fsUuid";
@@ -287,8 +284,6 @@
private ArrayMap<String, VolumeRecord> mRecords = new ArrayMap<>();
@GuardedBy("mLock")
private String mPrimaryStorageUuid;
- @GuardedBy("mLock")
- private boolean mForceAdoptable;
/** Map from disk ID to latches */
@GuardedBy("mLock")
@@ -1011,9 +1006,14 @@
@Override
public void onDiskCreated(String diskId, int flags) {
synchronized (mLock) {
- if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)
- || mForceAdoptable) {
- flags |= DiskInfo.FLAG_ADOPTABLE;
+ final String value = SystemProperties.get(StorageManager.PROP_ADOPTABLE);
+ switch (value) {
+ case "force_on":
+ flags |= DiskInfo.FLAG_ADOPTABLE;
+ break;
+ case "force_off":
+ flags &= ~DiskInfo.FLAG_ADOPTABLE;
+ break;
}
mDisks.put(diskId, new DiskInfo(diskId, flags));
}
@@ -1530,7 +1530,6 @@
private void readSettingsLocked() {
mRecords.clear();
mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
- mForceAdoptable = false;
FileInputStream fis = null;
try {
@@ -1552,7 +1551,6 @@
mPrimaryStorageUuid = readStringAttribute(in,
ATTR_PRIMARY_STORAGE_UUID);
}
- mForceAdoptable = readBooleanAttribute(in, ATTR_FORCE_ADOPTABLE, false);
} else if (TAG_VOLUME.equals(tag)) {
final VolumeRecord rec = readVolumeRecord(in);
@@ -1583,7 +1581,6 @@
out.startTag(null, TAG_VOLUMES);
writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
- writeBooleanAttribute(out, ATTR_FORCE_ADOPTABLE, mForceAdoptable);
final int size = mRecords.size();
for (int i = 0; i < size; i++) {
final VolumeRecord rec = mRecords.valueAt(i);
@@ -1980,12 +1977,25 @@
}
}
- if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
- synchronized (mLock) {
- mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0;
+ if ((mask & (StorageManager.DEBUG_ADOPTABLE_FORCE_ON
+ | StorageManager.DEBUG_ADOPTABLE_FORCE_OFF)) != 0) {
+ final String value;
+ if ((flags & StorageManager.DEBUG_ADOPTABLE_FORCE_ON) != 0) {
+ value = "force_on";
+ } else if ((flags & StorageManager.DEBUG_ADOPTABLE_FORCE_OFF) != 0) {
+ value = "force_off";
+ } else {
+ value = "";
+ }
- writeSettingsLocked();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ SystemProperties.set(StorageManager.PROP_ADOPTABLE, value);
+
+ // Reset storage to kick new setting into place
mHandler.obtainMessage(H_RESET).sendToTarget();
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@@ -3564,8 +3574,6 @@
pw.print(DataUnit.MEBIBYTES.toBytes(pair.second));
pw.println(" MiB)");
}
- pw.println("Force adoptable: " + mForceAdoptable);
- pw.println();
pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers));
pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6718d95..62a055d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -11451,6 +11451,19 @@
}
@Override
+ public void setSplitScreenResizing(boolean resizing) {
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setSplitScreenResizing()");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ mStackSupervisor.setSplitScreenResizing(resizing);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
final long ident = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e20356f..95bae2e 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1743,6 +1743,11 @@
return getDisplay().isTopStack(this);
}
+ boolean isTopActivityVisible() {
+ final ActivityRecord topActivity = getTopActivity();
+ return topActivity != null && topActivity.visible;
+ }
+
/**
* Returns true if the stack should be visible.
*
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6a3587c..0dc2445 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -43,6 +43,7 @@
import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.graphics.Rect.copyOrNull;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
@@ -245,6 +246,20 @@
// the activity callback indicating that it has completed pausing
static final boolean PAUSE_IMMEDIATELY = true;
+ /** True if the docked stack is currently being resized. */
+ private boolean mDockedStackResizing;
+
+ /**
+ * True if there are pending docked bounds that need to be applied after
+ * {@link #mDockedStackResizing} is reset to false.
+ */
+ private boolean mHasPendingDockedBounds;
+ private Rect mPendingDockedBounds;
+ private Rect mPendingTempDockedTaskBounds;
+ private Rect mPendingTempDockedTaskInsetBounds;
+ private Rect mPendingTempOtherTaskBounds;
+ private Rect mPendingTempOtherTaskInsetBounds;
+
/**
* The modes which affect which tasks are returned when calling
* {@link ActivityStackSupervisor#anyTaskForIdLocked(int)}.
@@ -2708,6 +2723,28 @@
moveTasksToFullscreenStackInSurfaceTransaction(fromStack, toDisplayId, onTop));
}
+ void setSplitScreenResizing(boolean resizing) {
+ if (resizing == mDockedStackResizing) {
+ return;
+ }
+
+ mDockedStackResizing = resizing;
+ mWindowManager.setDockedStackResizing(resizing);
+
+ if (!resizing && mHasPendingDockedBounds) {
+ resizeDockedStackLocked(mPendingDockedBounds, mPendingTempDockedTaskBounds,
+ mPendingTempDockedTaskInsetBounds, mPendingTempOtherTaskBounds,
+ mPendingTempOtherTaskInsetBounds, PRESERVE_WINDOWS);
+
+ mHasPendingDockedBounds = false;
+ mPendingDockedBounds = null;
+ mPendingTempDockedTaskBounds = null;
+ mPendingTempDockedTaskInsetBounds = null;
+ mPendingTempOtherTaskBounds = null;
+ mPendingTempOtherTaskInsetBounds = null;
+ }
+ }
+
void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
boolean preserveWindows) {
@@ -2731,6 +2768,15 @@
return;
}
+ if (mDockedStackResizing) {
+ mHasPendingDockedBounds = true;
+ mPendingDockedBounds = copyOrNull(dockedBounds);
+ mPendingTempDockedTaskBounds = copyOrNull(tempDockedTaskBounds);
+ mPendingTempDockedTaskInsetBounds = copyOrNull(tempDockedTaskInsetBounds);
+ mPendingTempOtherTaskBounds = copyOrNull(tempOtherTaskBounds);
+ mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds);
+ }
+
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
mWindowManager.deferSurfaceLayout();
try {
@@ -2765,6 +2811,11 @@
if (!current.affectedBySplitScreenResize()) {
continue;
}
+ if (mDockedStackResizing && !current.isTopActivityVisible()) {
+ // Non-visible stacks get resized once we're done with the resize
+ // interaction.
+ continue;
+ }
// Need to set windowing mode here before we try to get the dock bounds.
current.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
current.getStackDockedModeBounds(
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 40b9e4f..ed891df 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -134,6 +134,8 @@
30059 am_on_start_called (User|1|5),(Component Name|3),(Reason|3)
# The activity's onDestroy has been called.
30060 am_on_destroy_called (User|1|5),(Component Name|3),(Reason|3)
+# The activity's onActivityResult has been called.
+30062 am_on_activity_result_called (User|1|5),(Component Name|3),(Reason|3)
# The task is being removed from its parent stack
30061 am_remove_task (Task ID|1|5), (Stack ID|1|5)
\ No newline at end of file
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index ae7058d..9ef6c66 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -544,7 +544,8 @@
final int usedFlags = isRateLimitedForPoll(callingUid)
? flags & (~NetworkStatsManager.FLAG_POLL_ON_OPEN)
: flags;
- if ((usedFlags & NetworkStatsManager.FLAG_POLL_ON_OPEN) != 0) {
+ if ((usedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN
+ | NetworkStatsManager.FLAG_POLL_FORCE)) != 0) {
final long ident = Binder.clearCallingIdentity();
try {
performPoll(FLAG_PERSIST_ALL);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 4c8b91b..477b062 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -926,7 +926,11 @@
Slog.v(TAG, " disconnecting old " + getCaption() + ": " + info.service);
removeServiceLocked(i);
if (info.connection != null) {
- mContext.unbindService(info.connection);
+ try {
+ mContext.unbindService(info.connection);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "failed to unbind " + name, e);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index a24ac21..0dc06b2 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -554,7 +554,7 @@
return null;
}
- Animation loadAnimationAttr(LayoutParams lp, int animAttr) {
+ Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
int anim = 0;
Context context = mContext;
if (animAttr >= 0) {
@@ -564,6 +564,7 @@
anim = ent.array.getResourceId(animAttr, 0);
}
}
+ anim = updateToTranslucentAnimIfNeeded(anim, transit);
if (anim != 0) {
return AnimationUtils.loadAnimation(context, anim);
}
@@ -598,6 +599,16 @@
return null;
}
+ private int updateToTranslucentAnimIfNeeded(int anim, int transit) {
+ if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_OPEN && anim == R.anim.activity_open_enter) {
+ return R.anim.activity_translucent_open_enter;
+ }
+ if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE && anim == R.anim.activity_close_exit) {
+ return R.anim.activity_translucent_close_exit;
+ }
+ return anim;
+ }
+
/**
* Compute the pivot point for an animation that is scaling from a small
* rect on screen to a larger rect. The pivot point varies depending on
@@ -1664,29 +1675,17 @@
"applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS:"
+ " anim=" + a + " transit=" + appTransitionToString(transit)
+ " isEntrance=true" + " Callers=" + Debug.getCallers(3));
- } else if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_OPEN && enter) {
- a = loadAnimationRes("android",
- com.android.internal.R.anim.activity_translucent_open_enter);
- Slog.v(TAG,
- "applyAnimation TRANSIT_TRANSLUCENT_ACTIVITY_OPEN:"
- + " anim=" + a + " transit=" + appTransitionToString(transit)
- + " isEntrance=true" + " Callers=" + Debug.getCallers(3));
- } else if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE && !enter) {
- a = loadAnimationRes("android",
- com.android.internal.R.anim.activity_translucent_close_exit);
- Slog.v(TAG,
- "applyAnimation TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE:"
- + " anim=" + a + " transit=" + appTransitionToString(transit)
- + " isEntrance=false" + " Callers=" + Debug.getCallers(3));
} else {
int animAttr = 0;
switch (transit) {
case TRANSIT_ACTIVITY_OPEN:
+ case TRANSIT_TRANSLUCENT_ACTIVITY_OPEN:
animAttr = enter
? WindowAnimation_activityOpenEnterAnimation
: WindowAnimation_activityOpenExitAnimation;
break;
case TRANSIT_ACTIVITY_CLOSE:
+ case TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE:
animAttr = enter
? WindowAnimation_activityCloseEnterAnimation
: WindowAnimation_activityCloseExitAnimation;
@@ -1737,7 +1736,7 @@
? WindowAnimation_launchTaskBehindSourceAnimation
: WindowAnimation_launchTaskBehindTargetAnimation;
}
- a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
+ a = animAttr != 0 ? loadAnimationAttr(lp, animAttr, transit) : null;
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b1b026e..09e43f8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6864,7 +6864,6 @@
}
}
- @Override
public void setDockedStackResizing(boolean resizing) {
synchronized (mWindowMap) {
getDefaultDisplayContentLocked().getDockedDividerController().setResizing(resizing);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6af730f..6c2821d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2405,6 +2405,7 @@
@Override
public void binderDied() {
try {
+ boolean resetSplitScreenResizing = false;
synchronized(mService.mWindowMap) {
final WindowState win = mService.windowForClientLocked(mSession, mClient, false);
Slog.i(TAG, "WIN DEATH: " + win);
@@ -2424,13 +2425,23 @@
if (stack != null) {
stack.resetDockedStackToMiddle();
}
- mService.setDockedStackResizing(false);
+ resetSplitScreenResizing = true;
}
} else if (mHasSurface) {
Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
WindowState.this.removeIfPossible();
}
}
+ if (resetSplitScreenResizing) {
+ try {
+ // Note: this calls into ActivityManager, so we must *not* hold the window
+ // manager lock while calling this.
+ mService.mActivityManager.setSplitScreenResizing(false);
+ } catch (RemoteException e) {
+ // Local call, shouldn't return RemoteException.
+ throw e.rethrowAsRuntimeException();
+ }
+ }
} catch (IllegalArgumentException ex) {
// This will happen if the window has already been removed.
}
@@ -4706,16 +4717,38 @@
outPoint.offset(-mAttrs.surfaceInsets.left, -mAttrs.surfaceInsets.top);
}
+ boolean needsRelativeLayeringToIme() {
+ // We only use the relative layering mode in split screen, as part of elevating the IME
+ // and windows above it's target above the docked divider.
+ if (!inSplitScreenWindowingMode()) {
+ return false;
+ }
+
+ if (isChildWindow()) {
+ // If we are a child of the input method target we need this promotion.
+ if (getParentWindow().isInputMethodTarget()) {
+ return true;
+ }
+ } else if (mAppToken != null) {
+ // Likewise if we share a token with the Input method target and are ordered
+ // above it but not necessarily a child (e.g. a Dialog) then we also need
+ // this promotion.
+ final WindowState imeTarget = mService.mInputMethodTarget;
+ boolean inTokenWithAndAboveImeTarget = imeTarget != null && imeTarget != this
+ && imeTarget.mToken == mToken && imeTarget.compareTo(this) <= 0;
+ return inTokenWithAndAboveImeTarget;
+ }
+ return false;
+ }
+
@Override
void assignLayer(Transaction t, int layer) {
// See comment in assignRelativeLayerForImeTargetChild
- if (!isChildWindow()
- || (!getParentWindow().isInputMethodTarget())
- || !inSplitScreenWindowingMode()) {
- super.assignLayer(t, layer);
+ if (needsRelativeLayeringToIme()) {
+ getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
return;
}
- getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
+ super.assignLayer(t, layer);
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 195274a..a3428f0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -23,6 +23,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
@@ -1360,7 +1361,7 @@
break;
}
if (attr >= 0) {
- a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr);
+ a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr, TRANSIT_NONE);
}
}
if (DEBUG_ANIM) Slog.v(TAG,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 181fceb..ef9ba78 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -24,7 +24,13 @@
.USER_SENTIMENT_POSITIVE;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import android.app.INotificationManager;
import android.app.NotificationChannel;
import android.content.Intent;
import android.os.Binder;
@@ -34,6 +40,7 @@
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -52,6 +59,19 @@
private String[] mKeys = new String[] { "key", "key1", "key2", "key3"};
@Test
+ public void testGetActiveNotifications_notNull() throws Exception {
+ TestListenerService service = new TestListenerService();
+ INotificationManager noMan = service.getNoMan();
+ when(noMan.getActiveNotificationsFromListener(any(), any(), anyInt())).thenReturn(null);
+
+ assertNotNull(service.getActiveNotifications());
+ assertNotNull(service.getActiveNotifications(NotificationListenerService.TRIM_FULL));
+ assertNotNull(service.getActiveNotifications(new String[0]));
+ assertNotNull(service.getActiveNotifications(
+ new String[0], NotificationListenerService.TRIM_LIGHT));
+ }
+
+ @Test
public void testRanking() throws Exception {
TestListenerService service = new TestListenerService();
service.applyUpdateLocked(generateUpdate());
@@ -180,7 +200,12 @@
private final IBinder binder = new LocalBinder();
public TestListenerService() {
+ mWrapper = mock(NotificationListenerWrapper.class);
+ mNoMan = mock(INotificationManager.class);
+ }
+ INotificationManager getNoMan() {
+ return mNoMan;
}
@Override
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ba39ffd..19061f9 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1841,24 +1841,23 @@
}
/**
- * Returns the ISO country code equivalent of the current registered
- * operator's MCC (Mobile Country Code).
+ * Returns the ISO country code equivalent of the MCC (Mobile Country Code) of the current
+ * registered operator, or nearby cell information if not registered.
+ * .
* <p>
- * Availability: Only when user is registered to a network. Result may be
- * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
- * on a CDMA network).
+ * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
+ * if on a CDMA network).
*/
public String getNetworkCountryIso() {
return getNetworkCountryIsoForPhone(getPhoneId());
}
/**
- * Returns the ISO country code equivalent of the current registered
- * operator's MCC (Mobile Country Code) of a subscription.
+ * Returns the ISO country code equivalent of the MCC (Mobile Country Code) of the current
+ * registered operator, or nearby cell information if not registered.
* <p>
- * Availability: Only when user is registered to a network. Result may be
- * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
- * on a CDMA network).
+ * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
+ * if on a CDMA network).
*
* @param subId for which Network CountryIso is returned
* @hide
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index df483b2..d7a3771 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -306,6 +306,7 @@
}
// A value that represents a primitive data type (float, int, boolean, etc.).
+// Refer to Res_value in ResourceTypes.h for info on types and formatting
message Primitive {
message NullType {
}
@@ -315,8 +316,8 @@
NullType null_value = 1;
EmptyType empty_value = 2;
float float_value = 3;
- float dimension_value = 4;
- float fraction_value = 5;
+ uint32 dimension_value = 13;
+ uint32 fraction_value = 14;
int32 int_decimal_value = 6;
uint32 int_hexadecimal_value = 7;
bool boolean_value = 8;
@@ -324,6 +325,8 @@
uint32 color_rgb8_value = 10;
uint32 color_argb4_value = 11;
uint32 color_rgb4_value = 12;
+ float dimension_value_deprecated = 4 [deprecated=true];
+ float fraction_value_deprecated = 5 [deprecated=true];
}
}
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index f1eb952..3b101b7 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -780,13 +780,11 @@
} break;
case pb::Primitive::kDimensionValue: {
val.dataType = android::Res_value::TYPE_DIMENSION;
- float dimen_val = pb_prim.dimension_value();
- val.data = *(uint32_t*)&dimen_val;
+ val.data = pb_prim.dimension_value();
} break;
case pb::Primitive::kFractionValue: {
val.dataType = android::Res_value::TYPE_FRACTION;
- float fraction_val = pb_prim.fraction_value();
- val.data = *(uint32_t*)&fraction_val;
+ val.data = pb_prim.fraction_value();
} break;
case pb::Primitive::kIntDecimalValue: {
val.dataType = android::Res_value::TYPE_INT_DEC;
@@ -816,6 +814,16 @@
val.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
val.data = pb_prim.color_rgb4_value();
} break;
+ case pb::Primitive::kDimensionValueDeprecated: { // DEPRECATED
+ val.dataType = android::Res_value::TYPE_DIMENSION;
+ float dimen_val = pb_prim.dimension_value_deprecated();
+ val.data = *(uint32_t*)&dimen_val;
+ } break;
+ case pb::Primitive::kFractionValueDeprecated: { // DEPRECATED
+ val.dataType = android::Res_value::TYPE_FRACTION;
+ float fraction_val = pb_prim.fraction_value_deprecated();
+ val.data = *(uint32_t*)&fraction_val;
+ } break;
default: {
LOG(FATAL) << "Unexpected Primitive type: "
<< static_cast<uint32_t>(pb_prim.oneof_value_case());
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 2e56359..411cc29 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -452,10 +452,10 @@
pb_prim->set_float_value(*(float*)&val.data);
} break;
case android::Res_value::TYPE_DIMENSION: {
- pb_prim->set_dimension_value(*(float*)&val.data);
+ pb_prim->set_dimension_value(val.data);
} break;
case android::Res_value::TYPE_FRACTION: {
- pb_prim->set_fraction_value(*(float*)&val.data);
+ pb_prim->set_fraction_value(val.data);
} break;
case android::Res_value::TYPE_INT_DEC: {
pb_prim->set_int_decimal_value(static_cast<int32_t>(val.data));
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 6366a3d..21fdbd8 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -271,6 +271,7 @@
.AddValue("android:integer/hex_int_abcd", ResourceUtils::TryParseInt("0xABCD"))
.AddValue("android:dimen/dimen_1.39mm", ResourceUtils::TryParseFloat("1.39mm"))
.AddValue("android:fraction/fraction_27", ResourceUtils::TryParseFloat("27%"))
+ .AddValue("android:dimen/neg_2.3in", ResourceUtils::TryParseFloat("-2.3in"))
.AddValue("android:integer/null", ResourceUtils::MakeEmpty())
.Build();
@@ -353,6 +354,12 @@
EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_FRACTION));
EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseFloat("27%")->value.data));
+ bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:dimen/neg_2.3in",
+ ConfigDescription::DefaultConfig(), "");
+ ASSERT_THAT(bp, NotNull());
+ EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_DIMENSION));
+ EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseFloat("-2.3in")->value.data));
+
bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:integer/null",
ConfigDescription::DefaultConfig(), "");
ASSERT_THAT(bp, NotNull());
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 17819db..73b715a 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -98,9 +98,16 @@
name: "libstatslog",
generated_sources: ["statslog.cpp"],
generated_headers: ["statslog.h"],
+ srcs: [
+ "stats_event_list.cpp",
+ "statsd_writer.cpp",
+ ],
cflags: [
"-Wall",
"-Werror",
+ "-DLIBLOG_LOG_TAG=1006",
+ "-DWRITE_TO_STATSD=1",
+ "-DWRITE_TO_LOGD=0",
],
export_generated_headers: ["statslog.h"],
shared_libs: [
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 4146e02..638549d 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -104,7 +104,7 @@
fprintf(out, "#include <mutex>\n");
fprintf(out, "#include <chrono>\n");
fprintf(out, "#include <thread>\n");
- fprintf(out, "#include <log/log_event_list.h>\n");
+ fprintf(out, "#include <stats_event_list.h>\n");
fprintf(out, "#include <log/log.h>\n");
fprintf(out, "#include <statslog.h>\n");
fprintf(out, "#include <utils/SystemClock.h>\n");
@@ -242,7 +242,7 @@
fprintf(out, "{\n");
argIndex = 1;
- fprintf(out, " android_log_event_list event(kStatsEventTag);\n");
+ fprintf(out, " stats_event_list event(kStatsEventTag);\n");
fprintf(out, " event << android::elapsedRealtimeNano();\n\n");
fprintf(out, " event << code;\n\n");
for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -375,7 +375,7 @@
fprintf(out, "{\n");
argIndex = 1;
- fprintf(out, " android_log_event_list event(kStatsEventTag);\n");
+ fprintf(out, " stats_event_list event(kStatsEventTag);\n");
fprintf(out, " event << android::elapsedRealtimeNano();\n\n");
fprintf(out, " event << code;\n\n");
for (vector<java_type_t>::const_iterator arg = signature->begin();
diff --git a/tools/stats_log_api_gen/stats_event_list.cpp b/tools/stats_log_api_gen/stats_event_list.cpp
new file mode 100644
index 0000000..d456ef0
--- /dev/null
+++ b/tools/stats_log_api_gen/stats_event_list.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stats_event_list.h"
+
+#include "statsd_writer.h"
+
+namespace android {
+namespace util {
+
+enum ReadWriteFlag {
+ kAndroidLoggerRead = 1,
+ kAndroidLoggerWrite = 2,
+};
+
+typedef struct {
+ uint32_t tag;
+ unsigned pos; /* Read/write position into buffer */
+ unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements */
+ unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* pos for list counter */
+ unsigned list_nest_depth;
+ unsigned len; /* Length or raw buffer. */
+ bool overflow;
+ bool list_stop; /* next call decrement list_nest_depth and issue a stop */
+ ReadWriteFlag read_write_flag;
+ uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+} android_log_context_internal;
+
+extern struct android_log_transport_write statsdLoggerWrite;
+
+static int __write_to_statsd_init(struct iovec* vec, size_t nr);
+static int (*write_to_statsd)(struct iovec* vec,
+ size_t nr) = __write_to_statsd_init;
+
+int stats_write_list(android_log_context ctx) {
+ android_log_context_internal* context;
+ const char* msg;
+ ssize_t len;
+
+ context = (android_log_context_internal*)(ctx);
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+
+ if (context->list_nest_depth) {
+ return -EIO;
+ }
+
+ /* NB: if there was overflow, then log is truncated. Nothing reported */
+ context->storage[1] = context->count[0];
+ len = context->len = context->pos;
+ msg = (const char*)context->storage;
+ /* it's not a list */
+ if (context->count[0] <= 1) {
+ len -= sizeof(uint8_t) + sizeof(uint8_t);
+ if (len < 0) {
+ len = 0;
+ }
+ msg += sizeof(uint8_t) + sizeof(uint8_t);
+ }
+
+ struct iovec vec[2];
+ vec[0].iov_base = &context->tag;
+ vec[0].iov_len = sizeof(context->tag);
+ vec[1].iov_base = (void*)msg;
+ vec[1].iov_len = len;
+ return write_to_statsd(vec, 2);
+}
+
+int stats_event_list::write_to_logger(android_log_context ctx, log_id_t id) {
+ int retValue = 0;
+
+ if (WRITE_TO_LOGD) {
+ retValue = android_log_write_list(ctx, id);
+ }
+
+ if (WRITE_TO_STATSD) {
+ // log_event_list's cast operator is overloaded.
+ int ret = stats_write_list(static_cast<android_log_context>(*this));
+ // In debugging phase, we may write to both logd and statsd. Prefer to return
+ // statsd socket write error code here.
+ if (ret < 0) {
+ retValue = ret;
+ }
+ }
+
+ return retValue;
+}
+
+/* log_init_lock assumed */
+static int __write_to_statsd_initialize_locked() {
+ if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
+ if (statsdLoggerWrite.close) {
+ (*statsdLoggerWrite.close)();
+ return -ENODEV;
+ }
+ }
+ return 1;
+}
+
+static int __write_to_stats_daemon(struct iovec* vec, size_t nr) {
+ int ret, save_errno;
+ struct timespec ts;
+ size_t len, i;
+
+ for (len = i = 0; i < nr; ++i) {
+ len += vec[i].iov_len;
+ }
+ if (!len) {
+ return -EINVAL;
+ }
+
+ save_errno = errno;
+ clock_gettime(CLOCK_REALTIME, &ts);
+
+ ret = 0;
+
+ ssize_t retval;
+ retval = (*statsdLoggerWrite.write)(&ts, vec, nr);
+ if (ret >= 0) {
+ ret = retval;
+ }
+
+ errno = save_errno;
+ return ret;
+}
+
+static int __write_to_statsd_init(struct iovec* vec, size_t nr) {
+ int ret, save_errno = errno;
+
+ statsd_writer_init_lock();
+
+ if (write_to_statsd == __write_to_statsd_init) {
+ ret = __write_to_statsd_initialize_locked();
+ if (ret < 0) {
+ statsd_writer_init_unlock();
+ errno = save_errno;
+ return ret;
+ }
+
+ write_to_statsd = __write_to_stats_daemon;
+ }
+
+ statsd_writer_init_unlock();
+
+ ret = write_to_statsd(vec, nr);
+ errno = save_errno;
+ return ret;
+}
+
+} // namespace util
+} // namespace android
\ No newline at end of file
diff --git a/tools/stats_log_api_gen/stats_event_list.h b/tools/stats_log_api_gen/stats_event_list.h
new file mode 100644
index 0000000..66b9918
--- /dev/null
+++ b/tools/stats_log_api_gen/stats_event_list.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STATS_LOG_STATS_EVENT_LIST_H
+#define ANDROID_STATS_LOG_STATS_EVENT_LIST_H
+
+#include <log/log_event_list.h>
+
+namespace android {
+namespace util {
+
+/**
+ * A copy of android_log_event_list class.
+ *
+ * android_log_event_list is going to be deprecated soon, so copy it here to avoid creating
+ * dependency on upstream code. TODO(b/78304629): Rewrite this code.
+ */
+class stats_event_list {
+private:
+ android_log_context ctx;
+ int ret;
+
+ stats_event_list(const stats_event_list&) = delete;
+ void operator=(const stats_event_list&) = delete;
+
+ int write_to_logger(android_log_context context, log_id_t id);
+
+public:
+ explicit stats_event_list(int tag) : ret(0) {
+ ctx = create_android_logger(static_cast<uint32_t>(tag));
+ }
+ explicit stats_event_list(log_msg& log_msg) : ret(0) {
+ ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
+ log_msg.entry.len - sizeof(uint32_t));
+ }
+ ~stats_event_list() {
+ android_log_destroy(&ctx);
+ }
+
+ int close() {
+ int retval = android_log_destroy(&ctx);
+ if (retval < 0) ret = retval;
+ return retval;
+ }
+
+ /* To allow above C calls to use this class as parameter */
+ operator android_log_context() const {
+ return ctx;
+ }
+
+ /* return errors or transmit status */
+ int status() const {
+ return ret;
+ }
+
+ int begin() {
+ int retval = android_log_write_list_begin(ctx);
+ if (retval < 0) ret = retval;
+ return ret;
+ }
+ int end() {
+ int retval = android_log_write_list_end(ctx);
+ if (retval < 0) ret = retval;
+ return ret;
+ }
+
+ stats_event_list& operator<<(int32_t value) {
+ int retval = android_log_write_int32(ctx, value);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ stats_event_list& operator<<(uint32_t value) {
+ int retval = android_log_write_int32(ctx, static_cast<int32_t>(value));
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ stats_event_list& operator<<(bool value) {
+ int retval = android_log_write_int32(ctx, value ? 1 : 0);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ stats_event_list& operator<<(int64_t value) {
+ int retval = android_log_write_int64(ctx, value);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ stats_event_list& operator<<(uint64_t value) {
+ int retval = android_log_write_int64(ctx, static_cast<int64_t>(value));
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ stats_event_list& operator<<(const char* value) {
+ int retval = android_log_write_string8(ctx, value);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+#if defined(_USING_LIBCXX)
+ stats_event_list& operator<<(const std::string& value) {
+ int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+#endif
+
+ stats_event_list& operator<<(float value) {
+ int retval = android_log_write_float32(ctx, value);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ int write(log_id_t id = LOG_ID_EVENTS) {
+ /* facilitate -EBUSY retry */
+ if ((ret == -EBUSY) || (ret > 0)) ret = 0;
+ int retval = write_to_logger(ctx, id);
+ /* existing errors trump transmission errors */
+ if (!ret) ret = retval;
+ return ret;
+ }
+
+ int operator<<(log_id_t id) {
+ write(id);
+ android_log_destroy(&ctx);
+ return ret;
+ }
+
+ /*
+ * Append<Type> methods removes any integer promotion
+ * confusion, and adds access to string with length.
+ * Append methods are also added for all types for
+ * convenience.
+ */
+
+ bool AppendInt(int32_t value) {
+ int retval = android_log_write_int32(ctx, value);
+ if (retval < 0) ret = retval;
+ return ret >= 0;
+ }
+
+ bool AppendLong(int64_t value) {
+ int retval = android_log_write_int64(ctx, value);
+ if (retval < 0) ret = retval;
+ return ret >= 0;
+ }
+
+ bool AppendString(const char* value) {
+ int retval = android_log_write_string8(ctx, value);
+ if (retval < 0) ret = retval;
+ return ret >= 0;
+ }
+
+ bool AppendString(const char* value, size_t len) {
+ int retval = android_log_write_string8_len(ctx, value, len);
+ if (retval < 0) ret = retval;
+ return ret >= 0;
+ }
+
+#if defined(_USING_LIBCXX)
+ bool AppendString(const std::string& value) {
+ int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+ if (retval < 0) ret = retval;
+ return ret;
+ }
+
+ bool Append(const std::string& value) {
+ int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+ if (retval < 0) ret = retval;
+ return ret;
+ }
+#endif
+
+ bool AppendFloat(float value) {
+ int retval = android_log_write_float32(ctx, value);
+ if (retval < 0) ret = retval;
+ return ret >= 0;
+ }
+
+ template <typename Tvalue>
+ bool Append(Tvalue value) {
+ *this << value;
+ return ret >= 0;
+ }
+
+ bool Append(const char* value, size_t len) {
+ int retval = android_log_write_string8_len(ctx, value, len);
+ if (retval < 0) ret = retval;
+ return ret >= 0;
+ }
+
+ android_log_list_element read() {
+ return android_log_read_next(ctx);
+ }
+ android_log_list_element peek() {
+ return android_log_peek_next(ctx);
+ }
+};
+
+} // namespace util
+} // namespace android
+
+#endif // ANDROID_STATS_LOG_STATS_EVENT_LIST_H
diff --git a/tools/stats_log_api_gen/statsd_writer.cpp b/tools/stats_log_api_gen/statsd_writer.cpp
new file mode 100644
index 0000000..d736f7e
--- /dev/null
+++ b/tools/stats_log_api_gen/statsd_writer.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "statsd_writer.h"
+
+#include <cutils/sockets.h>
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+/* branchless on many architectures. */
+#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
+
+namespace android {
+namespace util {
+
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+
+void statsd_writer_init_lock() {
+ /*
+ * If we trigger a signal handler in the middle of locked activity and the
+ * signal handler logs a message, we could get into a deadlock state.
+ */
+ pthread_mutex_lock(&log_init_lock);
+}
+
+int statd_writer_trylock() {
+ return pthread_mutex_trylock(&log_init_lock);
+}
+
+void statsd_writer_init_unlock() {
+ pthread_mutex_unlock(&log_init_lock);
+}
+
+static int statsdAvailable();
+static int statsdOpen();
+static void statsdClose();
+static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
+
+struct android_log_transport_write statsdLoggerWrite = {
+ .name = "statsd",
+ .available = statsdAvailable,
+ .open = statsdOpen,
+ .close = statsdClose,
+ .write = statsdWrite,
+};
+
+std::atomic_int android_log_transport_write::sock(-EBADF);
+
+/* log_init_lock assumed */
+static int statsdOpen() {
+ int i, ret = 0;
+
+ i = atomic_load(&statsdLoggerWrite.sock);
+ if (i < 0) {
+ int sock = TEMP_FAILURE_RETRY(
+ socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ if (sock < 0) {
+ ret = -errno;
+ } else {
+ struct sockaddr_un un;
+ memset(&un, 0, sizeof(struct sockaddr_un));
+ un.sun_family = AF_UNIX;
+ strcpy(un.sun_path, "/dev/socket/statsdw");
+
+ if (TEMP_FAILURE_RETRY(connect(sock, (struct sockaddr*)&un,
+ sizeof(struct sockaddr_un))) < 0) {
+ ret = -errno;
+ switch (ret) {
+ case -ENOTCONN:
+ case -ECONNREFUSED:
+ case -ENOENT:
+ i = atomic_exchange(&statsdLoggerWrite.sock, ret);
+ /* FALLTHRU */
+ default:
+ break;
+ }
+ close(sock);
+ } else {
+ ret = atomic_exchange(&statsdLoggerWrite.sock, sock);
+ if ((ret >= 0) && (ret != sock)) {
+ close(ret);
+ }
+ ret = 0;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void __statsdClose(int negative_errno) {
+ int sock = atomic_exchange(&statsdLoggerWrite.sock, negative_errno);
+ if (sock >= 0) {
+ close(sock);
+ }
+}
+
+static void statsdClose() {
+ __statsdClose(-EBADF);
+}
+
+static int statsdAvailable() {
+ if (atomic_load(&statsdLoggerWrite.sock) < 0) {
+ if (access("/dev/socket/statsdw", W_OK) == 0) {
+ return 0;
+ }
+ return -EBADF;
+ }
+ return 1;
+}
+
+static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
+ ssize_t ret;
+ int sock;
+ static const unsigned headerLength = 1;
+ struct iovec newVec[nr + headerLength];
+ android_log_header_t header;
+ size_t i, payloadSize;
+ static atomic_int dropped;
+
+ sock = atomic_load(&statsdLoggerWrite.sock);
+ if (sock < 0)
+ switch (sock) {
+ case -ENOTCONN:
+ case -ECONNREFUSED:
+ case -ENOENT:
+ break;
+ default:
+ return -EBADF;
+ }
+ /*
+ * struct {
+ * // what we provide to socket
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ header.tid = gettid();
+ header.realtime.tv_sec = ts->tv_sec;
+ header.realtime.tv_nsec = ts->tv_nsec;
+
+ newVec[0].iov_base = (unsigned char*)&header;
+ newVec[0].iov_len = sizeof(header);
+
+ // If we dropped events before, try to tell statsd.
+ if (sock >= 0) {
+ int32_t snapshot =
+ atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+ if (snapshot) {
+ android_log_event_int_t buffer;
+ header.id = LOG_ID_STATS;
+ buffer.header.tag = htole32(LIBLOG_LOG_TAG);
+ buffer.payload.type = EVENT_TYPE_INT;
+ buffer.payload.data = htole32(snapshot);
+
+ newVec[headerLength].iov_base = &buffer;
+ newVec[headerLength].iov_len = sizeof(buffer);
+
+ ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
+ if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+ atomic_fetch_add_explicit(&dropped, snapshot,
+ memory_order_relaxed);
+ }
+ }
+ }
+
+ header.id = LOG_ID_STATS;
+
+ for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
+ newVec[i].iov_base = vec[i - headerLength].iov_base;
+ payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
+
+ if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
+ newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
+ if (newVec[i].iov_len) {
+ ++i;
+ }
+ break;
+ }
+ }
+
+ /*
+ * The write below could be lost, but will never block.
+ *
+ * ENOTCONN occurs if statsd has died.
+ * ENOENT occurs if statsd is not running and socket is missing.
+ * ECONNREFUSED occurs if we can not reconnect to statsd.
+ * EAGAIN occurs if statsd is overloaded.
+ */
+ if (sock < 0) {
+ ret = sock;
+ } else {
+ ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
+ if (ret < 0) {
+ ret = -errno;
+ }
+ }
+ switch (ret) {
+ case -ENOTCONN:
+ case -ECONNREFUSED:
+ case -ENOENT:
+ if (statd_writer_trylock()) {
+ return ret; /* in a signal handler? try again when less stressed
+ */
+ }
+ __statsdClose(ret);
+ ret = statsdOpen();
+ statsd_writer_init_unlock();
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = TEMP_FAILURE_RETRY(
+ writev(atomic_load(&statsdLoggerWrite.sock), newVec, i));
+ if (ret < 0) {
+ ret = -errno;
+ }
+ /* FALLTHRU */
+ default:
+ break;
+ }
+
+ if (ret > (ssize_t)sizeof(header)) {
+ ret -= sizeof(header);
+ } else if (ret == -EAGAIN) {
+ atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+ }
+
+ return ret;
+}
+
+} // namespace util
+} // namespace android
\ No newline at end of file
diff --git a/tools/stats_log_api_gen/statsd_writer.h b/tools/stats_log_api_gen/statsd_writer.h
new file mode 100644
index 0000000..05ebc6c
--- /dev/null
+++ b/tools/stats_log_api_gen/statsd_writer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STATS_LOG_STATS_WRITER_H
+#define ANDROID_STATS_LOG_STATS_WRITER_H
+
+#include <pthread.h>
+#include <stdatomic.h>
+#include <sys/socket.h>
+
+namespace android {
+namespace util {
+
+/**
+ * Internal lock should not be exposed. This is bad design.
+ * TODO: rewrite it in c++ code and encapsulate the functionality in a
+ * StatsdWriter class.
+ */
+void statsd_writer_init_lock();
+int statsd_writer_init_trylock();
+void statsd_writer_init_unlock();
+
+struct android_log_transport_write {
+ const char* name; /* human name to describe the transport */
+ static std::atomic_int sock;
+ int (*available)(); /* Does not cause resources to be taken */
+ int (*open)(); /* can be called multiple times, reusing current resources */
+ void (*close)(); /* free up resources */
+ /* write log to transport, returns number of bytes propagated, or -errno */
+ int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
+};
+
+} // namespace util
+} // namespace android
+
+#endif // ANDROID_STATS_LOG_STATS_WRITER_H