summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--core/java/android/net/ConnectivityMetricsEvent.aidl1
-rw-r--r--core/java/android/net/ConnectivityMetricsEvent.java38
-rw-r--r--core/java/android/net/ConnectivityMetricsLogger.java70
-rw-r--r--core/java/android/net/IConnectivityMetricsLogger.aidl23
-rw-r--r--core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl25
-rw-r--r--core/java/android/widget/Editor.java9
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java14
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java4
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java32
-rw-r--r--services/core/java/com/android/server/connectivity/MetricsLoggerService.java334
-rw-r--r--services/net/java/android/net/ip/IpManager.java15
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java10
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java6
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java59
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/scrolled.xml8
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java5
17 files changed, 466 insertions, 188 deletions
diff --git a/Android.mk b/Android.mk
index 1469c2cdf93e..fc9c319a996a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -197,7 +197,6 @@ LOCAL_SRC_FILES += \
core/java/android/net/ICaptivePortal.aidl \
core/java/android/net/IConnectivityManager.aidl \
core/java/android/net/IConnectivityMetricsLogger.aidl \
- core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl \
core/java/android/net/IEthernetManager.aidl \
core/java/android/net/IEthernetServiceListener.aidl \
core/java/android/net/INetworkManagementEventObserver.aidl \
diff --git a/core/java/android/net/ConnectivityMetricsEvent.aidl b/core/java/android/net/ConnectivityMetricsEvent.aidl
index da175614b588..a027d7c38140 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.aidl
+++ b/core/java/android/net/ConnectivityMetricsEvent.aidl
@@ -17,3 +17,4 @@
package android.net;
parcelable ConnectivityMetricsEvent;
+parcelable ConnectivityMetricsEvent.Reference;
diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java
index 098f1e6947a3..b5d67d38455d 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.java
+++ b/core/java/android/net/ConnectivityMetricsEvent.java
@@ -78,4 +78,42 @@ public final class ConnectivityMetricsEvent implements Parcelable {
return String.format("ConnectivityMetricsEvent(%d, %d, %d)", timestamp,
componentTag, eventTag);
}
+
+ /** {@hide} */
+ public static class Reference implements Parcelable {
+
+ public long value;
+
+ public Reference(long ref) {
+ this.value = ref;
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Parcelable.Creator<Reference> CREATOR
+ = new Parcelable.Creator<Reference> (){
+ public Reference createFromParcel(Parcel source) {
+ return new Reference(source.readLong());
+ }
+
+ public Reference[] newArray(int size) {
+ return new Reference[size];
+ }
+ };
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(value);
+ }
+
+ public void readFromParcel(Parcel in) {
+ value = in.readLong();
+ }
+ }
}
diff --git a/core/java/android/net/ConnectivityMetricsLogger.java b/core/java/android/net/ConnectivityMetricsLogger.java
index 3ef805017f14..eafb8acb8aeb 100644
--- a/core/java/android/net/ConnectivityMetricsLogger.java
+++ b/core/java/android/net/ConnectivityMetricsLogger.java
@@ -15,6 +15,7 @@
*/
package android.net;
+import android.os.Bundle;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -28,14 +29,24 @@ public class ConnectivityMetricsLogger {
public static final String CONNECTIVITY_METRICS_LOGGER_SERVICE = "connectivity_metrics_logger";
// Component Tags
- public static final int COMPONENT_TAG_CONNECTIVITY = 1;
- public static final int COMPONENT_TAG_BLUETOOTH = 2;
- public static final int COMPONENT_TAG_WIFI = 3;
- public static final int COMPONENT_TAG_TELECOM = 4;
- public static final int COMPONENT_TAG_TELEPHONY = 5;
+ public static final int COMPONENT_TAG_CONNECTIVITY = 0;
+ public static final int COMPONENT_TAG_BLUETOOTH = 1;
+ public static final int COMPONENT_TAG_WIFI = 2;
+ public static final int COMPONENT_TAG_TELECOM = 3;
+ public static final int COMPONENT_TAG_TELEPHONY = 4;
+
+ public static final int NUMBER_OF_COMPONENTS = 5;
+
+ // Event Tag
+ public static final int TAG_SKIPPED_EVENTS = -1;
+
+ public static final String DATA_KEY_EVENTS_COUNT = "count";
private IConnectivityMetricsLogger mService;
+ private long mServiceUnblockedTimestampMillis = 0;
+ private int mNumSkippedEvents = 0;
+
public ConnectivityMetricsLogger() {
mService = IConnectivityMetricsLogger.Stub.asInterface(ServiceManager.getService(
CONNECTIVITY_METRICS_LOGGER_SERVICE));
@@ -46,12 +57,51 @@ public class ConnectivityMetricsLogger {
if (DBG) {
Log.d(TAG, "logEvent(" + componentTag + "," + eventTag + ") Service not ready");
}
- } else {
- try {
- mService.logEvent(new ConnectivityMetricsEvent(timestamp, componentTag, eventTag, data));
- } catch (RemoteException e) {
- Log.e(TAG, "Error logging event " + e.getMessage());
+ return;
+ }
+
+ if (mServiceUnblockedTimestampMillis > 0) {
+ if (System.currentTimeMillis() < mServiceUnblockedTimestampMillis) {
+ // Service is throttling events.
+ // Don't send new events because they will be dropped.
+ mNumSkippedEvents++;
+ return;
+ }
+ }
+
+ ConnectivityMetricsEvent skippedEventsEvent = null;
+ if (mNumSkippedEvents > 0) {
+ // Log number of skipped events
+ Bundle b = new Bundle();
+ b.putInt(DATA_KEY_EVENTS_COUNT, mNumSkippedEvents);
+ skippedEventsEvent = new ConnectivityMetricsEvent(mServiceUnblockedTimestampMillis,
+ componentTag, TAG_SKIPPED_EVENTS, b);
+
+ mServiceUnblockedTimestampMillis = 0;
+ }
+
+ ConnectivityMetricsEvent event = new ConnectivityMetricsEvent(timestamp, componentTag,
+ eventTag, data);
+
+ try {
+ long result;
+ if (skippedEventsEvent == null) {
+ result = mService.logEvent(event);
+ } else {
+ result = mService.logEvents(new ConnectivityMetricsEvent[]
+ {skippedEventsEvent, event});
+ }
+
+ if (result == 0) {
+ mNumSkippedEvents = 0;
+ } else {
+ mNumSkippedEvents++;
+ if (result > 0) { // events are throttled
+ mServiceUnblockedTimestampMillis = result;
+ }
}
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error logging event " + e.getMessage());
}
}
}
diff --git a/core/java/android/net/IConnectivityMetricsLogger.aidl b/core/java/android/net/IConnectivityMetricsLogger.aidl
index 27786712a5c5..a83a01935253 100644
--- a/core/java/android/net/IConnectivityMetricsLogger.aidl
+++ b/core/java/android/net/IConnectivityMetricsLogger.aidl
@@ -16,15 +16,28 @@
package android.net;
+import android.app.PendingIntent;
import android.net.ConnectivityMetricsEvent;
-import android.net.IConnectivityMetricsLoggerSubscriber;
/** {@hide} */
interface IConnectivityMetricsLogger {
- void logEvent(in ConnectivityMetricsEvent event);
- void logEvents(in ConnectivityMetricsEvent[] events);
+ /**
+ * @return 0 on success
+ * <0 if error happened
+ * >0 timestamp after which new events will be accepted
+ */
+ long logEvent(in ConnectivityMetricsEvent event);
+ long logEvents(in ConnectivityMetricsEvent[] events);
- boolean subscribe(in IConnectivityMetricsLoggerSubscriber subscriber);
- void unsubscribe(in IConnectivityMetricsLoggerSubscriber subscriber);
+ /**
+ * @param reference of the last event previously returned. The function will return
+ * events following it.
+ * If 0 then all events will be returned.
+ * After the function call it will contain reference of the last event.
+ */
+ ConnectivityMetricsEvent[] getEvents(inout ConnectivityMetricsEvent.Reference reference);
+
+ boolean register(in PendingIntent newEventsIntent);
+ void unregister(in PendingIntent newEventsIntent);
}
diff --git a/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl b/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl
deleted file mode 100644
index a2c62cdaee97..000000000000
--- a/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.net.ConnectivityMetricsEvent;
-
-/** {@hide} */
-oneway interface IConnectivityMetricsLoggerSubscriber {
-
- void onEvents(in ConnectivityMetricsEvent[] events);
-}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 6585fd8b3143..7055f783c584 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1863,9 +1863,9 @@ public class Editor {
if (hasSelection) {
hideInsertionPointCursorController();
if (mTextActionMode == null) {
- if (mRestartActionModeOnNextRefresh || mTextView.isInExtractedMode()) {
+ if (mRestartActionModeOnNextRefresh) {
// To avoid distraction, newly start action mode only when selection action
- // mode is being restarted or in full screen extracted mode.
+ // mode is being restarted.
startSelectionActionMode();
}
} else if (selectionController == null || !selectionController.isActive()) {
@@ -4875,11 +4875,12 @@ public class Editor {
@Override
protected int getOffsetAtCoordinate(@NonNull Layout layout, int line, float x) {
- final int primaryOffset = layout.getOffsetForHorizontal(line, x, true);
+ final float localX = mTextView.convertToLocalHorizontalCoordinate(x);
+ final int primaryOffset = layout.getOffsetForHorizontal(line, localX, true);
if (!layout.isLevelBoundary(primaryOffset)) {
return primaryOffset;
}
- final int secondaryOffset = layout.getOffsetForHorizontal(line, x, false);
+ final int secondaryOffset = layout.getOffsetForHorizontal(line, localX, false);
final int currentOffset = getCurrentCursorOffset();
final int primaryDiff = Math.abs(primaryOffset - currentOffset);
final int secondaryDiff = Math.abs(secondaryOffset - currentOffset);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 341b2a440b48..c2bb4ebd09b7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -106,6 +106,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.Objects;
@@ -1063,8 +1064,19 @@ public class DirectoryFragment extends Fragment
}
public void selectAllFiles() {
+ // Exclude disabled files
+ List<String> enabled = new ArrayList<String>();
+ for (String id : mAdapter.getModelIds()) {
+ Cursor cursor = getModel().getItem(id);
+ String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+ if (isDocumentEnabled(docMimeType, docFlags)) {
+ enabled.add(id);
+ }
+ }
+
// Only select things currently visible in the adapter.
- boolean changed = mSelectionManager.setItemsSelected(mAdapter.getModelIds(), true);
+ boolean changed = mSelectionManager.setItemsSelected(enabled, true);
if (changed) {
updateDisplayState();
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
index faa8e3892f61..016cc9e889df 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -148,11 +148,15 @@ public abstract class FragmentTuner {
MenuItem share = menu.findItem(R.id.menu_share);
MenuItem delete = menu.findItem(R.id.menu_delete);
MenuItem rename = menu.findItem(R.id.menu_rename);
+ MenuItem selectAll = menu.findItem(R.id.menu_select_all);
open.setVisible(true);
share.setVisible(false);
delete.setVisible(false);
rename.setVisible(false);
+ selectAll.setVisible(mState.allowMultiple);
+
+ Menus.disableHiddenItems(menu);
}
@Override
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 95d3cc3bbcf0..a4d6be53a033 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -371,8 +371,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
private int mNetTransitionWakeLockTimeout;
private final PowerManager.WakeLock mPendingIntentWakeLock;
- private InetAddress mDefaultDns;
-
// used in DBG mode to track inet condition reports
private static final int INET_CONDITION_LOG_MAX_SIZE = 15;
private ArrayList mInetLog;
@@ -645,19 +643,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- // read our default dns server ip
- String dns = Settings.Global.getString(context.getContentResolver(),
- Settings.Global.DEFAULT_DNS_SERVER);
- if (dns == null || dns.length() == 0) {
- dns = context.getResources().getString(
- com.android.internal.R.string.config_default_dns_server);
- }
- try {
- mDefaultDns = NetworkUtils.numericToInetAddress(dns);
- } catch (IllegalArgumentException e) {
- loge("Error setting defaultDns using " + dns);
- }
-
mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
@@ -4149,14 +4134,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// }
updateTcpBufferSizes(networkAgent);
- // TODO: deprecate and remove mDefaultDns when we can do so safely. See http://b/18327075
- // In L, we used it only when the network had Internet access but provided no DNS servers.
- // For now, just disable it, and if disabling it doesn't break things, remove it.
- // final boolean useDefaultDns = networkAgent.networkCapabilities.hasCapability(
- // NET_CAPABILITY_INTERNET);
- final boolean useDefaultDns = false;
final boolean flushDns = updateRoutes(newLp, oldLp, netId);
- updateDnses(newLp, oldLp, netId, flushDns, useDefaultDns);
+ updateDnses(newLp, oldLp, netId, flushDns);
updateClat(newLp, oldLp, networkAgent);
if (isDefaultNetwork(networkAgent)) {
@@ -4260,16 +4239,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId,
- boolean flush, boolean useDefaultDns) {
+ boolean flush) {
if (oldLp == null || (newLp.isIdenticalDnses(oldLp) == false)) {
Collection<InetAddress> dnses = newLp.getDnsServers();
- if (dnses.size() == 0 && mDefaultDns != null && useDefaultDns) {
- dnses = new ArrayList();
- dnses.add(mDefaultDns);
- if (DBG) {
- loge("no dns provided for netId " + netId + ", so using defaults");
- }
- }
if (DBG) log("Setting Dns servers for network " + netId + " to " + dnses);
try {
mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses),
diff --git a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
index 7cac2270950d..f91db7813d42 100644
--- a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
+++ b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
@@ -18,18 +18,21 @@ package com.android.server.connectivity;
import com.android.server.SystemService;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.net.ConnectivityMetricsEvent;
import android.net.ConnectivityMetricsLogger;
import android.net.IConnectivityMetricsLogger;
-import android.net.IConnectivityMetricsLoggerSubscriber;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.ArrayMap;
+import android.os.Binder;
+import android.os.Parcel;
+import android.text.format.DateUtils;
import android.util.Log;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.List;
/** {@hide} */
public class MetricsLoggerService extends SystemService {
@@ -43,134 +46,307 @@ public class MetricsLoggerService extends SystemService {
@Override
public void onStart() {
+ resetThrottlingCounters(System.currentTimeMillis());
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
- Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
+ if (DBG) Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
publishBinderService(ConnectivityMetricsLogger.CONNECTIVITY_METRICS_LOGGER_SERVICE,
mBinder);
}
}
- private final int MAX_NUMBER_OF_EVENTS = 100;
- private final int MAX_TIME_OFFSET = 15*60*1000; // 15 minutes
- private final List<ConnectivityMetricsEvent> mEvents = new ArrayList<>();
- private long mLastSentEventTimeMillis = System.currentTimeMillis();
+ // TODO: read from system property
+ private final int MAX_NUMBER_OF_EVENTS = 1000;
- private final void enforceConnectivityInternalPermission() {
+ // TODO: read from system property
+ private final int EVENTS_NOTIFICATION_THRESHOLD = 300;
+
+ // TODO: read from system property
+ private final int THROTTLING_TIME_INTERVAL_MILLIS = 60 * 60 * 1000; // 1 hour
+
+ // TODO: read from system property
+ private final int THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT = 1000;
+
+ private int mEventCounter = 0;
+
+ /**
+ * Reference of the last event in the list of cached events.
+ *
+ * When client of this service retrieves events by calling getEvents, it is passing
+ * ConnectivityMetricsEvent.Reference object. After getEvents returns, that object will
+ * contain this reference. The client can save it and use next time it calls getEvents.
+ * This way only new events will be returned.
+ */
+ private long mLastEventReference = 0;
+
+ private final int mThrottlingCounters[] =
+ new int[ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS];
+
+ private long mThrottlingIntervalBoundaryMillis;
+
+ private final ArrayDeque<ConnectivityMetricsEvent> mEvents = new ArrayDeque<>();
+
+ private void enforceConnectivityInternalPermission() {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.CONNECTIVITY_INTERNAL,
"MetricsLoggerService");
}
+ private void enforceDumpPermission() {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DUMP,
+ "MetricsLoggerService");
+ }
+
+ private void resetThrottlingCounters(long currentTimeMillis) {
+ for (int i = 0; i < mThrottlingCounters.length; i++) {
+ mThrottlingCounters[i] = 0;
+ }
+ mThrottlingIntervalBoundaryMillis =
+ currentTimeMillis + THROTTLING_TIME_INTERVAL_MILLIS;
+ }
+
+ private void addEvent(ConnectivityMetricsEvent e) {
+ if (VDBG) {
+ Log.v(TAG, "writeEvent(" + e.toString() + ")");
+ }
+
+ while (mEvents.size() >= MAX_NUMBER_OF_EVENTS) {
+ mEvents.removeFirst();
+ }
+
+ mEvents.addLast(e);
+ }
+
/**
* Implementation of the IConnectivityMetricsLogger interface.
*/
private final IConnectivityMetricsLogger.Stub mBinder = new IConnectivityMetricsLogger.Stub() {
- private final ArrayMap<IConnectivityMetricsLoggerSubscriber,
- IBinder.DeathRecipient> mSubscribers = new ArrayMap<>();
-
-
- private ConnectivityMetricsEvent[] prepareEventsToSendIfReady() {
- ConnectivityMetricsEvent[] eventsToSend = null;
- final long currentTimeMillis = System.currentTimeMillis();
- final long timeOffset = currentTimeMillis - mLastSentEventTimeMillis;
- if (timeOffset >= MAX_TIME_OFFSET
- || timeOffset < 0 // system time has changed
- || mEvents.size() >= MAX_NUMBER_OF_EVENTS) {
- // batch events
- mLastSentEventTimeMillis = currentTimeMillis;
- eventsToSend = new ConnectivityMetricsEvent[mEvents.size()];
- mEvents.toArray(eventsToSend);
- mEvents.clear();
+ private final ArrayList<PendingIntent> mPendingIntents = new ArrayList<>();
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump ConnectivityMetricsLoggerService " +
+ "from from pid=" + Binder.getCallingPid() + ", uid=" +
+ Binder.getCallingUid());
+ return;
}
- return eventsToSend;
- }
- private void maybeSendEventsToSubscribers(ConnectivityMetricsEvent[] eventsToSend) {
- if (eventsToSend == null || eventsToSend.length == 0) return;
- synchronized (mSubscribers) {
- for (IConnectivityMetricsLoggerSubscriber s : mSubscribers.keySet()) {
- try {
- s.onEvents(eventsToSend);
- } catch (RemoteException ex) {
- Log.e(TAG, "RemoteException " + ex);
+ boolean dumpSerializedSize = false;
+ boolean dumpEvents = false;
+ for (String arg : args) {
+ switch (arg) {
+ case "--events":
+ dumpEvents = true;
+ break;
+
+ case "--size":
+ dumpSerializedSize = true;
+ break;
+
+ case "--all":
+ dumpEvents = true;
+ dumpSerializedSize = true;
+ break;
+ }
+ }
+
+ synchronized (mEvents) {
+ pw.println("Number of events: " + mEvents.size());
+ pw.println("Time span: " +
+ DateUtils.formatElapsedTime(
+ (System.currentTimeMillis() - mEvents.peekFirst().timestamp)
+ / 1000));
+
+ if (dumpSerializedSize) {
+ long dataSize = 0;
+ Parcel p = Parcel.obtain();
+ for (ConnectivityMetricsEvent e : mEvents) {
+ dataSize += 16; // timestamp and 2 stamps
+
+ p.writeParcelable(e.data, 0);
+ }
+ dataSize += p.dataSize();
+ p.recycle();
+ pw.println("Serialized data size: " + dataSize);
+ }
+
+ if (dumpEvents) {
+ pw.println();
+ pw.println("Events:");
+ for (ConnectivityMetricsEvent e : mEvents) {
+ pw.println(e.toString());
}
}
}
+
+ if (!mPendingIntents.isEmpty()) {
+ pw.println();
+ pw.println("Pending intents:");
+ for (PendingIntent pi : mPendingIntents) {
+ pw.println(pi.toString());
+ }
+ }
}
- public void logEvent(ConnectivityMetricsEvent event) {
+ public long logEvent(ConnectivityMetricsEvent event) {
ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event};
- logEvents(events);
+ return logEvents(events);
}
- public void logEvents(ConnectivityMetricsEvent[] events) {
+ /**
+ * @param events
+ *
+ * Note: All events must belong to the same component.
+ *
+ * @return 0 on success
+ * <0 if error happened
+ * >0 timestamp after which new events will be accepted
+ */
+ public long logEvents(ConnectivityMetricsEvent[] events) {
enforceConnectivityInternalPermission();
- ConnectivityMetricsEvent[] eventsToSend;
- if (VDBG) {
- for (ConnectivityMetricsEvent e : events) {
- Log.v(TAG, "writeEvent(" + e.toString() + ")");
+ if (events == null || events.length == 0) {
+ Log.wtf(TAG, "No events passed to logEvents()");
+ return -1;
+ }
+
+ int componentTag = events[0].componentTag;
+ if (componentTag < 0 ||
+ componentTag >= ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS) {
+ Log.wtf(TAG, "Unexpected tag: " + componentTag);
+ return -1;
+ }
+
+ synchronized (mThrottlingCounters) {
+ long currentTimeMillis = System.currentTimeMillis();
+ if (currentTimeMillis > mThrottlingIntervalBoundaryMillis) {
+ resetThrottlingCounters(currentTimeMillis);
+ }
+
+ mThrottlingCounters[componentTag] += events.length;
+
+ if (mThrottlingCounters[componentTag] >
+ THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT) {
+ Log.w(TAG, "Too many events from #" + componentTag +
+ ". Block until " + mThrottlingIntervalBoundaryMillis);
+
+ return mThrottlingIntervalBoundaryMillis;
}
}
+ boolean sendPendingIntents = false;
+
synchronized (mEvents) {
for (ConnectivityMetricsEvent e : events) {
- mEvents.add(e);
+ if (e.componentTag != componentTag) {
+ Log.wtf(TAG, "Unexpected tag: " + e.componentTag);
+ return -1;
+ }
+
+ addEvent(e);
}
- eventsToSend = prepareEventsToSendIfReady();
+ mLastEventReference += events.length;
+
+ mEventCounter += events.length;
+ if (mEventCounter >= EVENTS_NOTIFICATION_THRESHOLD) {
+ mEventCounter = 0;
+ sendPendingIntents = true;
+ }
+ }
+
+ if (sendPendingIntents) {
+ synchronized (mPendingIntents) {
+ for (PendingIntent pi : mPendingIntents) {
+ if (VDBG) Log.v(TAG, "Send pending intent");
+ try {
+ pi.send(getContext(), 0, null, null, null);
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent canceled: " + pi);
+ mPendingIntents.remove(pi);
+ }
+ }
+ }
}
- maybeSendEventsToSubscribers(eventsToSend);
+ return 0;
}
- public boolean subscribe(IConnectivityMetricsLoggerSubscriber subscriber) {
- enforceConnectivityInternalPermission();
- if (VDBG) Log.v(TAG, "subscribe");
+ /**
+ * Retrieve events
+ *
+ * @param reference of the last event previously returned. The function will return
+ * events following it.
+ * If 0 then all events will be returned.
+ * After the function call it will contain reference of the
+ * last returned event.
+ * @return events
+ */
+ public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) {
+ enforceDumpPermission();
+ long ref = reference.value;
+ if (VDBG) Log.v(TAG, "getEvents(" + ref + ")");
- synchronized (mSubscribers) {
- if (mSubscribers.containsKey(subscriber)) {
- Log.e(TAG, "subscriber is already subscribed");
- return false;
+ ConnectivityMetricsEvent[] result;
+ synchronized (mEvents) {
+ if (ref > mLastEventReference) {
+ Log.e(TAG, "Invalid reference");
+ reference.value = mLastEventReference;
+ return null;
}
- final IConnectivityMetricsLoggerSubscriber s = subscriber;
- IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- if (VDBG) Log.v(TAG, "subscriber died");
- synchronized (mSubscribers) {
- mSubscribers.remove(s);
- }
+ if (ref < mLastEventReference - mEvents.size()) {
+ ref = mLastEventReference - mEvents.size();
+ }
+
+ int numEventsToSkip =
+ mEvents.size() // Total number of events
+ - (int)(mLastEventReference - ref); // Number of events to return
+
+ result = new ConnectivityMetricsEvent[mEvents.size() - numEventsToSkip];
+ int i = 0;
+ for (ConnectivityMetricsEvent e : mEvents) {
+ if (numEventsToSkip > 0) {
+ numEventsToSkip--;
+ } else {
+ result[i++] = e;
}
- };
-
- try {
- subscriber.asBinder().linkToDeath(dr, 0);
- mSubscribers.put(subscriber, dr);
- } catch (RemoteException e) {
- Log.e(TAG, "subscribe failed: " + e);
- return false;
}
}
+ reference.value = mLastEventReference;
+
+ return result;
+ }
+
+ public boolean register(PendingIntent newEventsIntent) {
+ enforceDumpPermission();
+ if (VDBG) Log.v(TAG, "register(" + newEventsIntent + ")");
+
+ synchronized (mPendingIntents) {
+ if (mPendingIntents.remove(newEventsIntent)) {
+ Log.w(TAG, "Replacing registered pending intent");
+ }
+ mPendingIntents.add(newEventsIntent);
+ }
+
return true;
}
- public void unsubscribe(IConnectivityMetricsLoggerSubscriber subscriber) {
- enforceConnectivityInternalPermission();
- if (VDBG) Log.v(TAG, "unsubscribe");
- synchronized (mSubscribers) {
- IBinder.DeathRecipient dr = mSubscribers.remove(subscriber);
- if (dr == null) {
- Log.e(TAG, "subscriber is not subscribed");
- return;
+ public void unregister(PendingIntent newEventsIntent) {
+ enforceDumpPermission();
+ if (VDBG) Log.v(TAG, "unregister(" + newEventsIntent + ")");
+
+ synchronized (mPendingIntents) {
+ if (!mPendingIntents.remove(newEventsIntent)) {
+ Log.e(TAG, "Pending intent is not registered");
}
- subscriber.asBinder().unlinkToDeath(dr, 0);
}
}
};
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 371ed1199824..54aeb3db7319 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -100,10 +100,6 @@ public class IpManager extends StateMachine {
public void onProvisioningSuccess(LinkProperties newLp) {}
public void onProvisioningFailure(LinkProperties newLp) {}
- // This is called whenever 464xlat is being enabled or disabled (i.e.
- // started or stopped).
- public void on464XlatChange(boolean enabled) {}
-
// Invoked on LinkProperties changes.
public void onLinkPropertiesChange(LinkProperties newLp) {}
@@ -120,6 +116,10 @@ public class IpManager extends StateMachine {
// If multicast filtering cannot be accomplished with APF, this function will be called to
// actuate multicast filtering using another means.
public void setFallbackMulticastFilter(boolean enabled) {}
+
+ // Enabled/disable Neighbor Discover offload functionality. This is
+ // called, for example, whenever 464xlat is being started or stopped.
+ public void setNeighborDiscoveryOffload(boolean enable) {}
}
public static class WaitForProvisioningCallback extends Callback {
@@ -304,7 +304,7 @@ public class IpManager extends StateMachine {
public void interfaceAdded(String iface) {
super.interfaceAdded(iface);
if (mClatInterfaceName.equals(iface)) {
- mCallback.on464XlatChange(true);
+ mCallback.setNeighborDiscoveryOffload(false);
}
}
@@ -312,7 +312,10 @@ public class IpManager extends StateMachine {
public void interfaceRemoved(String iface) {
super.interfaceRemoved(iface);
if (mClatInterfaceName.equals(iface)) {
- mCallback.on464XlatChange(false);
+ // TODO: consider sending a message to the IpManager main
+ // StateMachine thread, in case "NDO enabled" state becomes
+ // tied to more things that 464xlat operation.
+ mCallback.setNeighborDiscoveryOffload(true);
}
}
};
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index c8e3d03169e8..9e50ee898769 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -24,6 +24,7 @@ import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.Result.Status;
import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
import com.android.layoutlib.bridge.impl.RenderDrawable;
import com.android.layoutlib.bridge.impl.RenderSessionImpl;
import com.android.layoutlib.bridge.util.DynamicIdMap;
@@ -408,7 +409,9 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
/**
* Starts a layout session by inflating and rendering it. The method returns a
* {@link RenderSession} on which further actions can be taken.
- *
+ * <p/>
+ * If {@link SessionParams} includes the {@link RenderParamsFlags#FLAG_DO_NOT_RENDER_ON_CREATE},
+ * this method will only inflate the layout but will NOT render it.
* @param params the {@link SessionParams} object with all the information necessary to create
* the scene.
* @return a new {@link RenderSession} object that contains the result of the layout.
@@ -424,7 +427,10 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
lastResult = scene.init(params.getTimeout());
if (lastResult.isSuccess()) {
lastResult = scene.inflate();
- if (lastResult.isSuccess()) {
+
+ boolean doNotRenderOnCreate = Boolean.TRUE.equals(
+ params.getFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE));
+ if (lastResult.isSuccess() && !doNotRenderOnCreate) {
lastResult = scene.render(true /*freshRender*/);
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
index bd17a2fe6ca2..051de9055042 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
@@ -53,6 +53,12 @@ public final class RenderParamsFlags {
*/
public static final Key<Boolean> FLAG_KEY_XML_FILE_PARSER_SUPPORT =
new Key<Boolean>("xmlFileParser", Boolean.class);
+ /**
+ * To tell LayoutLib to not render when creating a new session. This allows controlling when the first
+ * layout rendering will happen.
+ */
+ public static final Key<Boolean> FLAG_DO_NOT_RENDER_ON_CREATE =
+ new Key<Boolean>("doNotRenderOnCreate", Boolean.class);
// Disallow instances.
private RenderParamsFlags() {}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 016825ae66e2..ce7104ee6f9b 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -267,6 +267,34 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
/**
+ * Renders the given view hierarchy to the passed canvas and returns the result of the render
+ * operation.
+ * @param canvas an optional canvas to render the views to. If null, only the measure and
+ * layout steps will be executed.
+ */
+ private static Result render(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot,
+ @Nullable Canvas canvas, int width, int height) {
+ // measure again with the size we need
+ // This must always be done before the call to layout
+ measureView(viewRoot, null /*measuredView*/,
+ width, MeasureSpec.EXACTLY,
+ height, MeasureSpec.EXACTLY);
+
+ // now do the layout.
+ viewRoot.layout(0, 0, width, height);
+ handleScrolling(context, viewRoot);
+
+ if (canvas == null) {
+ return SUCCESS.createResult();
+ }
+
+ AttachInfo_Accessor.dispatchOnPreDraw(viewRoot);
+ viewRoot.draw(canvas);
+
+ return SUCCESS.createResult();
+ }
+
+ /**
* Renders the scene.
* <p>
* {@link #acquire(long)} must have been called before this.
@@ -367,24 +395,12 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
}
- // measure again with the size we need
- // This must always be done before the call to layout
- measureView(mViewRoot, null /*measuredView*/,
- mMeasuredScreenWidth, MeasureSpec.EXACTLY,
- mMeasuredScreenHeight, MeasureSpec.EXACTLY);
-
- // now do the layout.
- mViewRoot.layout(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
-
- handleScrolling(mViewRoot);
-
+ Result renderResult = SUCCESS.createResult();
if (params.isLayoutOnly()) {
// delete the canvas and image to reset them on the next full rendering
mImage = null;
mCanvas = null;
} else {
- AttachInfo_Accessor.dispatchOnPreDraw(mViewRoot);
-
// draw the views
// create the BufferedImage into which the layout will be rendered.
boolean newImage = false;
@@ -446,6 +462,9 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
if (mElapsedFrameTimeNanos >= 0) {
long initialTime = System_Delegate.nanoTime();
if (!mFirstFrameExecuted) {
+ // We need to run an initial draw call to initialize the animations
+ render(getContext(), mViewRoot, mCanvas, 0, 0);
+
// The first frame will initialize the animations
Choreographer_Delegate.doFrame(initialTime);
mFirstFrameExecuted = true;
@@ -453,14 +472,15 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// Second frame will move the animations
Choreographer_Delegate.doFrame(initialTime + mElapsedFrameTimeNanos);
}
- mViewRoot.draw(mCanvas);
+ renderResult = render(getContext(), mViewRoot, mCanvas, mMeasuredScreenWidth,
+ mMeasuredScreenHeight);
}
mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(),
false);
// success!
- return SUCCESS.createResult();
+ return renderResult;
} catch (Throwable e) {
// get the real cause of the exception.
Throwable t = e;
@@ -488,7 +508,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
* @return the measured width/height if measuredView is non-null, null otherwise.
*/
@SuppressWarnings("deprecation") // For the use of Pair
- private Pair<Integer, Integer> measureView(ViewGroup viewToMeasure, View measuredView,
+ private static Pair<Integer, Integer> measureView(ViewGroup viewToMeasure, View measuredView,
int width, int widthMode, int height, int heightMode) {
int w_spec = MeasureSpec.makeMeasureSpec(width, widthMode);
int h_spec = MeasureSpec.makeMeasureSpec(height, heightMode);
@@ -1061,8 +1081,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
* the component supports nested scrolling attempt that first, then use the unconsumed scroll
* part to scroll the content in the component.
*/
- private void handleScrolling(View view) {
- BridgeContext context = getContext();
+ private static void handleScrolling(BridgeContext context, View view) {
int scrollPosX = context.getScrollXPos(view);
int scrollPosY = context.getScrollYPos(view);
if (scrollPosX != 0 || scrollPosY != 0) {
@@ -1080,7 +1099,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
}
if (scrollPosX != 0 || scrollPosY != 0) {
- view.scrollBy(scrollPosX, scrollPosY);
+ view.scrollTo(scrollPosX, scrollPosY);
}
}
@@ -1090,7 +1109,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
ViewGroup group = (ViewGroup) view;
for (int i = 0; i < group.getChildCount(); i++) {
View child = group.getChildAt(i);
- handleScrolling(child);
+ handleScrolling(context, child);
}
}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/scrolled.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/scrolled.xml
index a5ebc2e20847..a07498cd07b1 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/scrolled.xml
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/scrolled.xml
@@ -2,8 +2,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:scrollX="10px"
- android:scrollY="30px">
+ android:scrollX="30px"
+ android:scrollY="90px">
<LinearLayout
android:layout_width="60dp"
android:layout_height="60dp"
@@ -29,8 +29,8 @@
android:layout_width="200dp"
android:layout_height="400dp"
android:orientation="vertical"
- android:scrollX="-30px"
- android:scrollY="150px">
+ android:scrollX="-90px"
+ android:scrollY="450px">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="60dp"
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index 09dd5f078b9a..034c8b2db7da 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -30,6 +30,7 @@ import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.io.FolderWrapper;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
import com.android.layoutlib.bridge.impl.RenderAction;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
@@ -564,7 +565,7 @@ public class Main {
sFrameworkRepo.getConfiguredResources(config),
themeName, isProjectTheme);
- return new SessionParams(
+ SessionParams sessionParams = new SessionParams(
layoutParser,
renderingMode,
null /*used for caching*/,
@@ -574,6 +575,8 @@ public class Main {
0,
targetSdk,
getLayoutLog());
+ sessionParams.setFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE, true);
+ return sessionParams;
}
private static LayoutLog getLayoutLog() {