Merge "Reinflate QS view on Locale configuration change" into nyc-dev
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index de70139..0153a40 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -18,6 +18,7 @@
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
 import android.app.Dialog;
@@ -59,6 +60,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
+import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.SeekBar;
@@ -92,6 +94,7 @@
     public static final String SHOW_FULL_ZEN = "sysui_show_full_zen";
 
     private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
+    private static final int UPDATE_ANIMATION_DURATION = 80;
 
     private final Context mContext;
     private final H mHandler = new H();
@@ -369,6 +372,14 @@
         writer.println(mAccessibility.mFeedbackEnabled);
     }
 
+    private static int getImpliedLevel(SeekBar seekBar, int progress) {
+        final int m = seekBar.getMax();
+        final int n = m / 100 - 1;
+        final int level = progress == 0 ? 0
+                : progress == m ? (m / 100) : (1 + (int)((progress / (float) m) * n));
+        return level;
+    }
+
     @SuppressLint("InflateParams")
     private void initRow(final VolumeRow row, final int stream, int iconRes, int iconMuteRes,
             boolean important) {
@@ -707,7 +718,7 @@
                 : false;
 
         // update slider max
-        final int max = ss.levelMax;
+        final int max = ss.levelMax * 100;
         if (max != row.slider.getMax()) {
             row.slider.setMax(max);
         }
@@ -824,7 +835,8 @@
         if (row.tracking) {
             return;  // don't update if user is sliding
         }
-        final int level = row.slider.getProgress();
+        final int progress = row.slider.getProgress();
+        final int level = getImpliedLevel(row.slider, progress);
         final boolean rowVisible = row.view.getVisibility() == View.VISIBLE;
         final boolean inGracePeriod = (SystemClock.uptimeMillis() - row.userAttempt)
                 < USER_ATTEMPT_GRACE_PERIOD;
@@ -840,7 +852,33 @@
                 return;  // don't clamp if visible
             }
         }
-        row.slider.setProgress(vlevel, true);
+        final int newProgress = vlevel * 100;
+        if (progress != newProgress) {
+            if (mShowing && rowVisible) {
+                // animate!
+                if (row.anim != null && row.anim.isRunning()
+                        && row.animTargetProgress == newProgress) {
+                    return;  // already animating to the target progress
+                }
+                // start/update animation
+                if (row.anim == null) {
+                    row.anim = ObjectAnimator.ofInt(row.slider, "progress", progress, newProgress);
+                    row.anim.setInterpolator(new DecelerateInterpolator());
+                } else {
+                    row.anim.cancel();
+                    row.anim.setIntValues(progress, newProgress);
+                }
+                row.animTargetProgress = newProgress;
+                row.anim.setDuration(UPDATE_ANIMATION_DURATION);
+                row.anim.start();
+            } else {
+                // update slider directly to clamped value
+                if (row.anim != null) {
+                    row.anim.cancel();
+                }
+                row.slider.setProgress(newProgress);
+            }
+        }
     }
 
     private void recheckH(VolumeRow row) {
@@ -1060,19 +1098,20 @@
                     + " onProgressChanged " + progress + " fromUser=" + fromUser);
             if (!fromUser) return;
             if (mRow.ss.levelMin > 0) {
-                final int minProgress = mRow.ss.levelMin;
+                final int minProgress = mRow.ss.levelMin * 100;
                 if (progress < minProgress) {
                     seekBar.setProgress(minProgress);
                     progress = minProgress;
                 }
             }
-            if (mRow.ss.level != progress || mRow.ss.muted && progress > 0) {
+            final int userLevel = getImpliedLevel(seekBar, progress);
+            if (mRow.ss.level != userLevel || mRow.ss.muted && userLevel > 0) {
                 mRow.userAttempt = SystemClock.uptimeMillis();
-                if (mRow.requestedLevel != progress) {
-                    mController.setStreamVolume(mRow.stream, progress);
-                    mRow.requestedLevel = progress;
+                if (mRow.requestedLevel != userLevel) {
+                    mController.setStreamVolume(mRow.stream, userLevel);
+                    mRow.requestedLevel = userLevel;
                     Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_CHANGED, mRow.stream,
-                            progress);
+                            userLevel);
                 }
             }
         }
@@ -1089,7 +1128,7 @@
             if (D.BUG) Log.d(TAG, "onStopTrackingTouch"+ " " + mRow.stream);
             mRow.tracking = false;
             mRow.userAttempt = SystemClock.uptimeMillis();
-            final int userLevel = seekBar.getProgress();
+            final int userLevel = getImpliedLevel(seekBar, seekBar.getProgress());
             Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_DONE, mRow.stream, userLevel);
             if (mRow.ss.level != userLevel) {
                 mHandler.sendMessageDelayed(mHandler.obtainMessage(H.RECHECK, mRow),
@@ -1169,6 +1208,8 @@
         private ColorStateList cachedSliderTint;
         private int iconState;  // from Events
         private boolean cachedShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS;
+        private ObjectAnimator anim;  // slider progress animation for non-touch-related updates
+        private int animTargetProgress;
         private int lastAudibleLevel = 1;
     }
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0f21796..b965f64 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2330,6 +2330,18 @@
             if (VDBG || (DBG && nri.isRequest())) log("releasing NetworkRequest " + request);
             nri.unlinkDeathRecipient();
             mNetworkRequests.remove(request);
+            synchronized (mUidToNetworkRequestCount) {
+                int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
+                if (requests < 1) {
+                    Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " +
+                            nri.mUid);
+                } else if (requests == 1) {
+                    mUidToNetworkRequestCount.removeAt(
+                            mUidToNetworkRequestCount.indexOfKey(nri.mUid));
+                } else {
+                    mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
+                }
+            }
             mNetworkRequestInfoLogs.log("RELEASE " + nri);
             if (nri.isRequest()) {
                 // Find all networks that are satisfying this request and remove the request
@@ -3677,6 +3689,11 @@
     private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
             new HashMap<NetworkRequest, NetworkRequestInfo>();
 
+    private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
+    // Map from UID to number of NetworkRequests that UID has filed.
+    @GuardedBy("mUidToNetworkRequestCount")
+    private final SparseIntArray mUidToNetworkRequestCount = new SparseIntArray();
+
     private static class NetworkFactoryInfo {
         public final String name;
         public final Messenger messenger;
@@ -3737,6 +3754,7 @@
             mPid = getCallingPid();
             mUid = getCallingUid();
             mType = type;
+            enforceRequestCountLimit();
         }
 
         NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, NetworkRequestType type) {
@@ -3748,6 +3766,7 @@
             mUid = getCallingUid();
             mType = type;
             mPendingIntent = null;
+            enforceRequestCountLimit();
 
             try {
                 mBinder.linkToDeath(this, 0);
@@ -3756,6 +3775,16 @@
             }
         }
 
+        private void enforceRequestCountLimit() {
+            synchronized (mUidToNetworkRequestCount) {
+                int networkRequests = mUidToNetworkRequestCount.get(mUid, 0) + 1;
+                if (networkRequests >= MAX_NETWORK_REQUESTS_PER_UID) {
+                    throw new IllegalArgumentException("Too many NetworkRequests filed");
+                }
+                mUidToNetworkRequestCount.put(mUid, networkRequests);
+            }
+        }
+
         private String typeString() {
             switch (mType) {
                 case LISTEN: return "Listen";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d7afc13..d002c15 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17159,6 +17159,7 @@
                     || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
                     || Intent.ACTION_GET_PERMISSIONS_COUNT.equals(action)
                     || Intent.ACTION_GET_PERMISSIONS_PACKAGES.equals(action)
+                    || Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)
                     || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                     || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
                     || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) {
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 23f186c..7017d81 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -42,6 +42,7 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
+    <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 26eed24..4fae4a7 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -74,6 +74,7 @@
 import com.android.server.net.NetworkPinner;
 
 import java.net.InetAddress;
+import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
@@ -1788,4 +1789,96 @@
         waitFor(cv);
         assertPinnedToWifiWithCellDefault();
     }
+
+    @SmallTest
+    public void testNetworkRequestMaximum() {
+        final int MAX_REQUESTS = 100;
+        // Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added.
+        NetworkRequest networkRequest = new NetworkRequest.Builder().build();
+        ArrayList<NetworkCallback> networkCallbacks = new ArrayList<NetworkCallback>();
+        try {
+            for (int i = 0; i < MAX_REQUESTS; i++) {
+                NetworkCallback networkCallback = new NetworkCallback();
+                mCm.requestNetwork(networkRequest, networkCallback);
+                networkCallbacks.add(networkCallback);
+            }
+            fail("Registering " + MAX_REQUESTS + " NetworkRequests did not throw exception");
+        } catch (IllegalArgumentException expected) {}
+        for (NetworkCallback networkCallback : networkCallbacks) {
+            mCm.unregisterNetworkCallback(networkCallback);
+        }
+        networkCallbacks.clear();
+
+        try {
+            for (int i = 0; i < MAX_REQUESTS; i++) {
+                NetworkCallback networkCallback = new NetworkCallback();
+                mCm.registerNetworkCallback(networkRequest, networkCallback);
+                networkCallbacks.add(networkCallback);
+            }
+            fail("Registering " + MAX_REQUESTS + " NetworkCallbacks did not throw exception");
+        } catch (IllegalArgumentException expected) {}
+        for (NetworkCallback networkCallback : networkCallbacks) {
+            mCm.unregisterNetworkCallback(networkCallback);
+        }
+        networkCallbacks.clear();
+
+        ArrayList<PendingIntent> pendingIntents = new ArrayList<PendingIntent>();
+        try {
+            for (int i = 0; i < MAX_REQUESTS + 1; i++) {
+                PendingIntent pendingIntent =
+                        PendingIntent.getBroadcast(mContext, 0, new Intent("a" + i), 0);
+                mCm.requestNetwork(networkRequest, pendingIntent);
+                pendingIntents.add(pendingIntent);
+            }
+            fail("Registering " + MAX_REQUESTS +
+                    " PendingIntent NetworkRequests did not throw exception");
+        } catch (IllegalArgumentException expected) {}
+        for (PendingIntent pendingIntent : pendingIntents) {
+            mCm.unregisterNetworkCallback(pendingIntent);
+        }
+        pendingIntents.clear();
+
+        try {
+            for (int i = 0; i < MAX_REQUESTS + 1; i++) {
+                PendingIntent pendingIntent =
+                        PendingIntent.getBroadcast(mContext, 0, new Intent("a" + i), 0);
+                mCm.registerNetworkCallback(networkRequest, pendingIntent);
+                pendingIntents.add(pendingIntent);
+            }
+            fail("Registering " + MAX_REQUESTS +
+                    " PendingIntent NetworkCallbacks did not throw exception");
+        } catch (IllegalArgumentException expected) {}
+        for (PendingIntent pendingIntent : pendingIntents) {
+            mCm.unregisterNetworkCallback(pendingIntent);
+        }
+        pendingIntents.clear();
+        mService.waitForIdle(5000);
+
+        // Test that the limit is not hit when MAX_REQUESTS requests are added and removed.
+        for (int i = 0; i < MAX_REQUESTS; i++) {
+            NetworkCallback networkCallback = new NetworkCallback();
+            mCm.requestNetwork(networkRequest, networkCallback);
+            mCm.unregisterNetworkCallback(networkCallback);
+        }
+        mService.waitForIdle();
+        for (int i = 0; i < MAX_REQUESTS; i++) {
+            NetworkCallback networkCallback = new NetworkCallback();
+            mCm.registerNetworkCallback(networkRequest, networkCallback);
+            mCm.unregisterNetworkCallback(networkCallback);
+        }
+        mService.waitForIdle();
+        for (int i = 0; i < MAX_REQUESTS; i++) {
+            PendingIntent pendingIntent =
+                    PendingIntent.getBroadcast(mContext, 0, new Intent("b" + i), 0);
+            mCm.requestNetwork(networkRequest, pendingIntent);
+            mCm.unregisterNetworkCallback(pendingIntent);
+        }
+        mService.waitForIdle();
+        for (int i = 0; i < MAX_REQUESTS; i++) {
+            PendingIntent pendingIntent =
+                    PendingIntent.getBroadcast(mContext, 0, new Intent("c" + i), 0);
+            mCm.registerNetworkCallback(networkRequest, pendingIntent);
+            mCm.unregisterNetworkCallback(pendingIntent);
+        }
+    }
 }