Merge "Fixing where call goes from disconnecting to active to disconnecting state." into lmp-dev
diff --git a/Android.mk b/Android.mk
index 6a9da2f..b323650 100644
--- a/Android.mk
+++ b/Android.mk
@@ -187,7 +187,6 @@
 	core/java/android/nfc/INfcAdapterExtras.aidl \
 	core/java/android/nfc/INfcTag.aidl \
 	core/java/android/nfc/INfcCardEmulation.aidl \
-	core/java/android/nfc/INfcLockscreenDispatch.aidl \
 	core/java/android/nfc/INfcUnlockHandler.aidl \
 	core/java/android/os/IBatteryPropertiesListener.aidl \
 	core/java/android/os/IBatteryPropertiesRegistrar.aidl \
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 68606836..1e1a613 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -80,6 +80,9 @@
     private final static boolean DEBUG = false;
     private final static boolean DEBUG_ICONS = false;
 
+    // Default flags to use with PackageManager when no flags are given.
+    private final static int sDefaultFlags = PackageManager.GET_SHARED_LIBRARY_FILES;
+
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
@@ -730,7 +733,7 @@
         }
         if (appInfo == null) {
             try {
-                appInfo = getApplicationInfo(packageName, 0);
+                appInfo = getApplicationInfo(packageName, sDefaultFlags);
             } catch (NameNotFoundException e) {
                 return null;
             }
@@ -770,7 +773,7 @@
 
     @Override public Drawable getActivityIcon(ComponentName activityName)
             throws NameNotFoundException {
-        return getActivityInfo(activityName, 0).loadIcon(this);
+        return getActivityInfo(activityName, sDefaultFlags).loadIcon(this);
     }
 
     @Override public Drawable getActivityIcon(Intent intent)
@@ -799,13 +802,13 @@
 
     @Override public Drawable getApplicationIcon(String packageName)
             throws NameNotFoundException {
-        return getApplicationIcon(getApplicationInfo(packageName, 0));
+        return getApplicationIcon(getApplicationInfo(packageName, sDefaultFlags));
     }
 
     @Override
     public Drawable getActivityBanner(ComponentName activityName)
             throws NameNotFoundException {
-        return getActivityInfo(activityName, 0).loadBanner(this);
+        return getActivityInfo(activityName, sDefaultFlags).loadBanner(this);
     }
 
     @Override
@@ -832,13 +835,13 @@
     @Override
     public Drawable getApplicationBanner(String packageName)
             throws NameNotFoundException {
-        return getApplicationBanner(getApplicationInfo(packageName, 0));
+        return getApplicationBanner(getApplicationInfo(packageName, sDefaultFlags));
     }
 
     @Override
     public Drawable getActivityLogo(ComponentName activityName)
             throws NameNotFoundException {
-        return getActivityInfo(activityName, 0).loadLogo(this);
+        return getActivityInfo(activityName, sDefaultFlags).loadLogo(this);
     }
 
     @Override
@@ -865,7 +868,7 @@
     @Override
     public Drawable getApplicationLogo(String packageName)
             throws NameNotFoundException {
-        return getApplicationLogo(getApplicationInfo(packageName, 0));
+        return getApplicationLogo(getApplicationInfo(packageName, sDefaultFlags));
     }
 
     @Override
@@ -914,7 +917,7 @@
     @Override public Resources getResourcesForActivity(
         ComponentName activityName) throws NameNotFoundException {
         return getResourcesForApplication(
-            getActivityInfo(activityName, 0).applicationInfo);
+            getActivityInfo(activityName, sDefaultFlags).applicationInfo);
     }
 
     @Override public Resources getResourcesForApplication(
@@ -926,7 +929,8 @@
         Resources r = mContext.mMainThread.getTopLevelResources(
                 sameUid ? app.sourceDir : app.publicSourceDir,
                 sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
-                app.resourceDirs, null, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
+                app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
+                null, mContext.mPackageInfo);
         if (r != null) {
             return r;
         }
@@ -936,7 +940,7 @@
     @Override public Resources getResourcesForApplication(
         String appPackageName) throws NameNotFoundException {
         return getResourcesForApplication(
-            getApplicationInfo(appPackageName, 0));
+            getApplicationInfo(appPackageName, sDefaultFlags));
     }
 
     /** @hide */
@@ -951,7 +955,7 @@
             return mContext.mMainThread.getSystemContext().getResources();
         }
         try {
-            ApplicationInfo ai = mPM.getApplicationInfo(appPackageName, 0, userId);
+            ApplicationInfo ai = mPM.getApplicationInfo(appPackageName, sDefaultFlags, userId);
             if (ai != null) {
                 return getResourcesForApplication(ai);
             }
@@ -1136,7 +1140,7 @@
         }
         if (appInfo == null) {
             try {
-                appInfo = getApplicationInfo(packageName, 0);
+                appInfo = getApplicationInfo(packageName, sDefaultFlags);
             } catch (NameNotFoundException e) {
                 return null;
             }
@@ -1164,7 +1168,7 @@
                                     ApplicationInfo appInfo) {
         if (appInfo == null) {
             try {
-                appInfo = getApplicationInfo(packageName, 0);
+                appInfo = getApplicationInfo(packageName, sDefaultFlags);
             } catch (NameNotFoundException e) {
                 return null;
             }
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 9129121..5b926ad 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -26,7 +26,6 @@
 import android.nfc.INfcAdapterExtras;
 import android.nfc.INfcTag;
 import android.nfc.INfcCardEmulation;
-import android.nfc.INfcLockscreenDispatch;
 import android.nfc.INfcUnlockHandler;
 import android.os.Bundle;
 
diff --git a/core/java/android/nfc/INfcLockscreenDispatch.aidl b/core/java/android/nfc/INfcLockscreenDispatch.aidl
deleted file mode 100644
index 27e9a8c..0000000
--- a/core/java/android/nfc/INfcLockscreenDispatch.aidl
+++ /dev/null
@@ -1,12 +0,0 @@
-package android.nfc;
-
-import android.nfc.Tag;
-
-/**
- * @hide
- */
-interface INfcLockscreenDispatch {
-
-    boolean onTagDetected(in Tag tag);
-
-}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 37294e7..984f12f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -721,6 +721,39 @@
     }
 
     /**
+     * Creates a secondary user with the specified name and options and configures it with default
+     * restrictions.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * @param name the user's name
+     * @param flags flags that identify the type of user and other properties.
+     * @see UserInfo
+     *
+     * @return the UserInfo object for the created user, or null if the user could not be created.
+     * @hide
+     */
+    public UserInfo createSecondaryUser(String name, int flags) {
+        try {
+            UserInfo user = mService.createUser(name, flags);
+            if (user == null) {
+                return null;
+            }
+            Bundle userRestrictions = mService.getUserRestrictions(user.id);
+            addDefaultUserRestrictions(userRestrictions);
+            mService.setUserRestrictions(userRestrictions, user.id);
+            return user;
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not create a user", re);
+            return null;
+        }
+    }
+
+    private static void addDefaultUserRestrictions(Bundle restrictions) {
+        restrictions.putBoolean(DISALLOW_OUTGOING_CALLS, true);
+        restrictions.putBoolean(DISALLOW_SMS, true);
+    }
+
+    /**
      * Creates a user with the specified name and options as a profile of another user.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      *
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 5be6371..52b4649 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -18,10 +18,15 @@
 
 import java.lang.ref.WeakReference;
 import java.nio.ByteBuffer;
+import java.util.Iterator;
 
+import android.os.Binder;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.util.Log;
 
 /**
@@ -99,6 +104,8 @@
 
     private final static String TAG = "android.media.AudioRecord";
 
+    /** @hide */
+    public final static String SUBMIX_FIXED_VOLUME = "fixedVolume";
 
     //---------------------------------------------------------
     // Used exclusively by native code
@@ -184,6 +191,7 @@
      * AudioAttributes
      */
     private AudioAttributes mAudioAttributes;
+    private boolean mIsSubmixFullVolume = false;
 
     //---------------------------------------------------------
     // Constructor, Finalize
@@ -267,6 +275,18 @@
 
         mAudioAttributes = attributes;
 
+        // is this AudioRecord using REMOTE_SUBMIX at full volume?
+        if (mAudioAttributes.getCapturePreset() == MediaRecorder.AudioSource.REMOTE_SUBMIX) {
+            final Iterator<String> tagsIter = mAudioAttributes.getTags().iterator();
+            while (tagsIter.hasNext()) {
+                if (tagsIter.next().equalsIgnoreCase(SUBMIX_FIXED_VOLUME)) {
+                    mIsSubmixFullVolume = true;
+                    Log.v(TAG, "Will record from REMOTE_SUBMIX at full fixed volume");
+                    break;
+                }
+            }
+        }
+
         int rate = 0;
         if ((format.getPropertySetMask()
                 & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0)
@@ -419,7 +439,8 @@
 
     @Override
     protected void finalize() {
-        native_finalize();
+        // will cause stop() to be called, and if appropriate, will handle fixed volume recording
+        release();
     }
 
 
@@ -586,6 +607,7 @@
         // start recording
         synchronized(mRecordingStateLock) {
             if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) {
+                handleFullVolumeRec(true);
                 mRecordingState = RECORDSTATE_RECORDING;
             }
         }
@@ -608,6 +630,7 @@
         // start recording
         synchronized(mRecordingStateLock) {
             if (native_start(syncEvent.getType(), syncEvent.getAudioSessionId()) == SUCCESS) {
+                handleFullVolumeRec(true);
                 mRecordingState = RECORDSTATE_RECORDING;
             }
         }
@@ -625,11 +648,25 @@
 
         // stop recording
         synchronized(mRecordingStateLock) {
+            handleFullVolumeRec(false);
             native_stop();
             mRecordingState = RECORDSTATE_STOPPED;
         }
     }
 
+    private final IBinder mICallBack = new Binder();
+    private void handleFullVolumeRec(boolean starting) {
+        if (!mIsSubmixFullVolume) {
+            return;
+        }
+        final IBinder b = ServiceManager.getService(android.content.Context.AUDIO_SERVICE);
+        final IAudioService ias = IAudioService.Stub.asInterface(b);
+        try {
+            ias.forceRemoteSubmixFullVolume(starting, mICallBack);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error talking to AudioService when handling full submix volume", e);
+        }
+    }
 
     //---------------------------------------------------------
     // Audio data supply
@@ -880,6 +917,7 @@
             int sampleRate, int channelMask, int audioFormat,
             int buffSizeInBytes, int[] sessionId);
 
+    // TODO remove: implementation calls directly into implementation of native_release()
     private native final void native_finalize();
 
     private native final void native_release();
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 5c2abc53..96ce2c1 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -485,6 +485,7 @@
             AudioSystem.DEVICE_OUT_HDMI_ARC |
             AudioSystem.DEVICE_OUT_SPDIF |
             AudioSystem.DEVICE_OUT_AUX_LINE;
+    int mFullVolumeDevices = 0;
 
     // TODO merge orientation and rotation
     private final boolean mMonitorOrientation;
@@ -727,6 +728,10 @@
         }
     }
 
+    private void checkAllFixedVolumeDevices(int streamType) {
+        mStreamStates[streamType].checkFixedVolumeDevices();
+    }
+
     private void createStreamStates() {
         int numStreamTypes = AudioSystem.getNumStreamTypes();
         VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
@@ -1466,6 +1471,106 @@
         return mStreamStates[streamType].isMuted();
     }
 
+    private class RmtSbmxFullVolDeathHandler implements IBinder.DeathRecipient {
+        private IBinder mICallback; // To be notified of client's death
+
+        RmtSbmxFullVolDeathHandler(IBinder cb) {
+            mICallback = cb;
+            try {
+                cb.linkToDeath(this, 0/*flags*/);
+            } catch (RemoteException e) {
+                Log.e(TAG, "can't link to death", e);
+            }
+        }
+
+        boolean isHandlerFor(IBinder cb) {
+            return mICallback.equals(cb);
+        }
+
+        void forget() {
+            try {
+                mICallback.unlinkToDeath(this, 0/*flags*/);
+            } catch (NoSuchElementException e) {
+                Log.e(TAG, "error unlinking to death", e);
+            }
+        }
+
+        public void binderDied() {
+            Log.w(TAG, "Recorder with remote submix at full volume died " + mICallback);
+            forceRemoteSubmixFullVolume(false, mICallback);
+        }
+    }
+
+    /**
+     * call must be synchronized on mRmtSbmxFullVolDeathHandlers
+     * @return true if there is a registered death handler, false otherwise */
+    private boolean discardRmtSbmxFullVolDeathHandlerFor(IBinder cb) {
+        Iterator<RmtSbmxFullVolDeathHandler> it = mRmtSbmxFullVolDeathHandlers.iterator();
+        while (it.hasNext()) {
+            final RmtSbmxFullVolDeathHandler handler = it.next();
+            if (handler.isHandlerFor(cb)) {
+                handler.forget();
+                mRmtSbmxFullVolDeathHandlers.remove(handler);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** call synchronized on mRmtSbmxFullVolDeathHandlers */
+    private boolean hasRmtSbmxFullVolDeathHandlerFor(IBinder cb) {
+        Iterator<RmtSbmxFullVolDeathHandler> it = mRmtSbmxFullVolDeathHandlers.iterator();
+        while (it.hasNext()) {
+            if (it.next().isHandlerFor(cb)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private int mRmtSbmxFullVolRefCount = 0;
+    private ArrayList<RmtSbmxFullVolDeathHandler> mRmtSbmxFullVolDeathHandlers =
+            new ArrayList<RmtSbmxFullVolDeathHandler>();
+
+    public void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb) {
+        if (cb == null) {
+            return;
+        }
+        if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
+                        android.Manifest.permission.CAPTURE_AUDIO_OUTPUT))) {
+            Log.w(TAG, "Trying to call forceRemoteSubmixFullVolume() without CAPTURE_AUDIO_OUTPUT");
+            return;
+        }
+        synchronized(mRmtSbmxFullVolDeathHandlers) {
+            boolean applyRequired = false;
+            if (startForcing) {
+                if (!hasRmtSbmxFullVolDeathHandlerFor(cb)) {
+                    mRmtSbmxFullVolDeathHandlers.add(new RmtSbmxFullVolDeathHandler(cb));
+                    if (mRmtSbmxFullVolRefCount == 0) {
+                        mFullVolumeDevices |= AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+                        mFixedVolumeDevices |= AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+                        applyRequired = true;
+                    }
+                    mRmtSbmxFullVolRefCount++;
+                }
+            } else {
+                if (discardRmtSbmxFullVolDeathHandlerFor(cb) && (mRmtSbmxFullVolRefCount > 0)) {
+                    mRmtSbmxFullVolRefCount--;
+                    if (mRmtSbmxFullVolRefCount == 0) {
+                        mFullVolumeDevices &= ~AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+                        mFixedVolumeDevices &= ~AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+                        applyRequired = true;
+                    }
+                }
+            }
+            if (applyRequired) {
+                // Assumes only STREAM_MUSIC going through DEVICE_OUT_REMOTE_SUBMIX
+                checkAllFixedVolumeDevices(AudioSystem.STREAM_MUSIC);
+                mStreamStates[AudioSystem.STREAM_MUSIC].applyAllVolumes();
+            }
+        }
+    }
+
     /** @see AudioManager#setMasterMute(boolean, int) */
     public void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb) {
         if (mUseFixedVolume) {
@@ -3243,8 +3348,8 @@
             int index;
             if (isMuted()) {
                 index = 0;
-            } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
-                       mAvrcpAbsVolSupported) {
+            } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported)
+                    || ((device & mFullVolumeDevices) != 0)) {
                 index = (mIndexMax + 5)/10;
             } else {
                 index = (getIndex(device) + 5)/10;
@@ -3272,8 +3377,10 @@
                     if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
                         if (isMuted()) {
                             index = 0;
-                        } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
-                                mAvrcpAbsVolSupported) {
+                        } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
+                                mAvrcpAbsVolSupported)
+                                    || ((device & mFullVolumeDevices) != 0))
+                        {
                             index = (mIndexMax + 5)/10;
                         } else {
                             index = ((Integer)entry.getValue() + 5)/10;
@@ -3403,7 +3510,8 @@
                         Map.Entry entry = (Map.Entry)i.next();
                         int device = ((Integer)entry.getKey()).intValue();
                         int index = ((Integer)entry.getValue()).intValue();
-                        if (((device & mFixedVolumeDevices) != 0) && index != 0) {
+                        if (((device & mFullVolumeDevices) != 0)
+                                || (((device & mFixedVolumeDevices) != 0) && index != 0)) {
                             entry.setValue(mIndexMax);
                         }
                         applyDeviceVolume(device);
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 75fc03c..1c41432 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -56,6 +56,8 @@
 
     boolean isStreamMute(int streamType);
 
+    void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb);
+
     void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb);
 
     boolean isMasterMute();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 954046c..fcdbfc1 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -48,6 +48,8 @@
     private static SummaryStats sScreenOnPulsingStats;
     private static SummaryStats sScreenOnNotPulsingStats;
     private static SummaryStats sEmergencyCallStats;
+    private static SummaryStats sProxNearStats;
+    private static SummaryStats sProxFarStats;
 
     public static void tracePickupPulse(boolean withinVibrationThreshold) {
         if (!ENABLED) return;
@@ -88,6 +90,8 @@
                 sScreenOnPulsingStats = new SummaryStats();
                 sScreenOnNotPulsingStats = new SummaryStats();
                 sEmergencyCallStats = new SummaryStats();
+                sProxNearStats = new SummaryStats();
+                sProxFarStats = new SummaryStats();
                 log("init");
                 KeyguardUpdateMonitor.getInstance(context).registerCallback(sKeyguardCallback);
             }
@@ -133,6 +137,12 @@
         }
     }
 
+    public static void traceProximityResult(boolean near, long millis) {
+        if (!ENABLED) return;
+        log("proximityResult near=" + near + " millis=" + millis);
+        (near ? sProxNearStats : sProxFarStats).append();
+    }
+
     public static void dump(PrintWriter pw) {
         synchronized (DozeLog.class) {
             if (sMessages == null) return;
@@ -154,6 +164,8 @@
             sScreenOnPulsingStats.dump(pw, "Screen on (pulsing)");
             sScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)");
             sEmergencyCallStats.dump(pw, "Emergency call");
+            sProxNearStats.dump(pw, "Proximity (near)");
+            sProxFarStats.dump(pw, "Proximity (far)");
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 3afdc3d..f8c5e9c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -25,11 +25,15 @@
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.hardware.TriggerEvent;
 import android.hardware.TriggerEventListener;
 import android.media.AudioAttributes;
+import android.os.Handler;
 import android.os.PowerManager;
+import android.os.SystemClock;
 import android.os.Vibrator;
 import android.service.dreams.DreamService;
 import android.util.Log;
@@ -55,6 +59,7 @@
     private final String mTag = String.format(TAG + ".%08x", hashCode());
     private final Context mContext = this;
     private final DozeParameters mDozeParameters = new DozeParameters(mContext);
+    private final Handler mHandler = new Handler();
 
     private DozeHost mHost;
     private SensorManager mSensors;
@@ -197,33 +202,49 @@
             // Here we need a wakelock to stay awake until the pulse is finished.
             mWakeLock.acquire();
             mPulsing = true;
-            mHost.pulseWhileDozing(new DozeHost.PulseCallback() {
+            final long start = SystemClock.uptimeMillis();
+            new ProximityCheck() {
                 @Override
-                public void onPulseStarted() {
-                    if (mPulsing && mDreaming) {
-                        turnDisplayOn();
-                    }
-                }
-
-                @Override
-                public void onPulseFinished() {
-                    if (mPulsing && mDreaming) {
+                public void onProximityResult(int result) {
+                    // avoid pulsing in pockets
+                    final boolean isNear = result == RESULT_NEAR;
+                    DozeLog.traceProximityResult(isNear, SystemClock.uptimeMillis() - start);
+                    if (isNear) {
                         mPulsing = false;
-                        turnDisplayOff();
+                        mWakeLock.release();
+                        return;
                     }
-                    mWakeLock.release(); // needs to be unconditional to balance acquire
+
+                    // not in-pocket, continue pulsing
+                    mHost.pulseWhileDozing(new DozeHost.PulseCallback() {
+                        @Override
+                        public void onPulseStarted() {
+                            if (mPulsing && mDreaming) {
+                                turnDisplayOn();
+                            }
+                        }
+
+                        @Override
+                        public void onPulseFinished() {
+                            if (mPulsing && mDreaming) {
+                                mPulsing = false;
+                                turnDisplayOff();
+                            }
+                            mWakeLock.release(); // needs to be unconditional to balance acquire
+                        }
+                    });
                 }
-            });
+            }.check();
         }
     }
 
     private void turnDisplayOff() {
-        if (DEBUG) Log.d(TAG, "Display off");
+        if (DEBUG) Log.d(mTag, "Display off");
         setDozeScreenState(Display.STATE_OFF);
     }
 
     private void turnDisplayOn() {
-        if (DEBUG) Log.d(TAG, "Display on");
+        if (DEBUG) Log.d(mTag, "Display on");
         setDozeScreenState(mDisplayStateSupported ? Display.STATE_DOZE : Display.STATE_ON);
     }
 
@@ -270,24 +291,24 @@
     }
 
     private void resetNotificationResets() {
-        if (DEBUG) Log.d(TAG, "resetNotificationResets");
+        if (DEBUG) Log.d(mTag, "resetNotificationResets");
         mScheduleResetsRemaining = mDozeParameters.getPulseScheduleResets();
     }
 
     private void updateNotificationPulse() {
-        if (DEBUG) Log.d(TAG, "updateNotificationPulse");
+        if (DEBUG) Log.d(mTag, "updateNotificationPulse");
         if (!mDozeParameters.getPulseOnNotifications()) return;
         if (mScheduleResetsRemaining <= 0) {
-            if (DEBUG) Log.d(TAG, "No more schedule resets remaining");
+            if (DEBUG) Log.d(mTag, "No more schedule resets remaining");
             return;
         }
         final long now = System.currentTimeMillis();
         if ((now - mNotificationPulseTime) < mDozeParameters.getPulseDuration()) {
-            if (DEBUG) Log.d(TAG, "Recently updated, not resetting schedule");
+            if (DEBUG) Log.d(mTag, "Recently updated, not resetting schedule");
             return;
         }
         mScheduleResetsRemaining--;
-        if (DEBUG) Log.d(TAG, "mScheduleResetsRemaining = " + mScheduleResetsRemaining);
+        if (DEBUG) Log.d(mTag, "mScheduleResetsRemaining = " + mScheduleResetsRemaining);
         mNotificationPulseTime = now;
         rescheduleNotificationPulse(true /*predicate*/);
     }
@@ -302,31 +323,31 @@
     }
 
     private void rescheduleNotificationPulse(boolean predicate) {
-        if (DEBUG) Log.d(TAG, "rescheduleNotificationPulse predicate=" + predicate);
+        if (DEBUG) Log.d(mTag, "rescheduleNotificationPulse predicate=" + predicate);
         final PendingIntent notificationPulseIntent = notificationPulseIntent(0);
         mAlarmManager.cancel(notificationPulseIntent);
         if (!predicate) {
-            if (DEBUG) Log.d(TAG, "  don't reschedule: predicate is false");
+            if (DEBUG) Log.d(mTag, "  don't reschedule: predicate is false");
             return;
         }
         final PulseSchedule schedule = mDozeParameters.getPulseSchedule();
         if (schedule == null) {
-            if (DEBUG) Log.d(TAG, "  don't reschedule: schedule is null");
+            if (DEBUG) Log.d(mTag, "  don't reschedule: schedule is null");
             return;
         }
         final long now = System.currentTimeMillis();
         final long time = schedule.getNextTime(now, mNotificationPulseTime);
         if (time <= 0) {
-            if (DEBUG) Log.d(TAG, "  don't reschedule: time is " + time);
+            if (DEBUG) Log.d(mTag, "  don't reschedule: time is " + time);
             return;
         }
         final long delta = time - now;
         if (delta <= 0) {
-            if (DEBUG) Log.d(TAG, "  don't reschedule: delta is " + delta);
+            if (DEBUG) Log.d(mTag, "  don't reschedule: delta is " + delta);
             return;
         }
         final long instance = time - mNotificationPulseTime;
-        if (DEBUG) Log.d(TAG, "Scheduling pulse " + instance + " in " + delta + "ms for "
+        if (DEBUG) Log.d(mTag, "Scheduling pulse " + instance + " in " + delta + "ms for "
                 + new Date(time));
         mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, time, notificationPulseIntent(instance));
     }
@@ -404,7 +425,9 @@
         private final boolean mConfigured;
         private final boolean mDebugVibrate;
 
-        private boolean mEnabled;
+        private boolean mRequested;
+        private boolean mRegistered;
+        private boolean mDisabled;
 
         public TriggerSensor(int type, boolean configured, boolean debugVibrate) {
             mSensor = mSensors.getDefaultSensor(type);
@@ -413,19 +436,34 @@
         }
 
         public void setListening(boolean listen) {
+            if (mRequested == listen) return;
+            mRequested = listen;
+            updateListener();
+        }
+
+        public void setDisabled(boolean disabled) {
+            if (mDisabled == disabled) return;
+            mDisabled = disabled;
+            updateListener();
+        }
+
+        private void updateListener() {
             if (!mConfigured || mSensor == null) return;
-            if (listen) {
-                mEnabled = mSensors.requestTriggerSensor(this, mSensor);
-            } else if (mEnabled) {
+            if (mRequested && !mDisabled) {
+                mRegistered = mSensors.requestTriggerSensor(this, mSensor);
+            } else if (mRegistered) {
                 mSensors.cancelTriggerSensor(this, mSensor);
-                mEnabled = false;
+                mRegistered = false;
             }
         }
 
         @Override
         public String toString() {
-            return new StringBuilder("{mEnabled=").append(mEnabled).append(", mConfigured=")
-                    .append(mConfigured).append(", mDebugVibrate=").append(mDebugVibrate)
+            return new StringBuilder("{mRegistered=").append(mRegistered)
+                    .append(", mRequested=").append(mRequested)
+                    .append(", mDisabled=").append(mDisabled)
+                    .append(", mConfigured=").append(mConfigured)
+                    .append(", mDebugVibrate=").append(mDebugVibrate)
                     .append(", mSensor=").append(mSensor).append("}").toString();
         }
 
@@ -449,7 +487,8 @@
 
                 // reset the notification pulse schedule, but only if we think we were not triggered
                 // by a notification-related vibration
-                final long timeSinceNotification = System.currentTimeMillis() - mNotificationPulseTime;
+                final long timeSinceNotification = System.currentTimeMillis()
+                        - mNotificationPulseTime;
                 final boolean withinVibrationThreshold =
                         timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
                 if (withinVibrationThreshold) {
@@ -465,4 +504,73 @@
             }
         }
     }
+
+    private abstract class ProximityCheck implements SensorEventListener, Runnable {
+        private static final int TIMEOUT_DELAY_MS = 500;
+
+        protected static final int RESULT_UNKNOWN = 0;
+        protected static final int RESULT_NEAR = 1;
+        protected static final int RESULT_FAR = 2;
+
+        private final String mTag = DozeService.this.mTag + ".ProximityCheck";
+
+        private boolean mRegistered;
+        private boolean mFinished;
+        private float mMaxRange;
+
+        abstract public void onProximityResult(int result);
+
+        public void check() {
+            if (mFinished || mRegistered) return;
+            final Sensor sensor = mSensors.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+            if (sensor == null) {
+                if (DEBUG) Log.d(mTag, "No sensor found");
+                finishWithResult(RESULT_UNKNOWN);
+                return;
+            }
+            // the pickup sensor interferes with the prox event, disable it until we have a result
+            mPickupSensor.setDisabled(true);
+
+            mMaxRange = sensor.getMaximumRange();
+            mSensors.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL, 0, mHandler);
+            mHandler.postDelayed(this, TIMEOUT_DELAY_MS);
+            mRegistered = true;
+        }
+
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            if (event.values.length == 0) {
+                if (DEBUG) Log.d(mTag, "Event has no values!");
+                finishWithResult(RESULT_UNKNOWN);
+            } else {
+                if (DEBUG) Log.d(mTag, "Event: value=" + event.values[0] + " max=" + mMaxRange);
+                final boolean isNear = event.values[0] < mMaxRange;
+                finishWithResult(isNear ? RESULT_NEAR : RESULT_FAR);
+            }
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG) Log.d(mTag, "No event received before timeout");
+            finishWithResult(RESULT_UNKNOWN);
+        }
+
+        private void finishWithResult(int result) {
+            if (mFinished) return;
+            if (mRegistered) {
+                mHandler.removeCallbacks(this);
+                mSensors.unregisterListener(this);
+                // we're done - reenable the pickup sensor
+                mPickupSensor.setDisabled(false);
+                mRegistered = false;
+            }
+            onProximityResult(result);
+            mFinished = true;
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+            // noop
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 9984fca..ce99cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -66,7 +66,8 @@
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        state.visible = mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed();
+        state.visible = mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed()
+                && !mController.isProvisioningNeeded();
         state.label = mContext.getString(R.string.quick_settings_hotspot_label);
 
         state.value = mController.isHotspotEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 582d165..0825aa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -17,9 +17,11 @@
 package com.android.systemui.statusbar;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.animation.Interpolator;
+import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -31,6 +33,12 @@
     }
 
     @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        ((TextView) findViewById(R.id.no_notifications)).setText(R.string.empty_shade_text);
+    }
+
+    @Override
     protected View findContentView() {
         return findViewById(R.id.no_notifications);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
index 7ca91a5..0863c86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
@@ -22,6 +22,7 @@
     boolean isHotspotEnabled();
     boolean isHotspotSupported();
     void setHotspotEnabled(boolean enabled);
+    boolean isProvisioningNeeded();
 
     public interface Callback {
         void onHotspotChanged(boolean enabled);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index dd706bb9..63c1100 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -24,6 +24,7 @@
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
@@ -74,6 +75,20 @@
     }
 
     @Override
+    public boolean isProvisioningNeeded() {
+        // Keep in sync with other usage of config_mobile_hotspot_provision_app.
+        // TetherSettings#isProvisioningNeeded and
+        // ConnectivityManager#enforceTetherChangePermission
+        String[] provisionApp = mContext.getResources().getStringArray(
+                com.android.internal.R.array.config_mobile_hotspot_provision_app);
+        if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)
+                || provisionApp == null) {
+            return false;
+        }
+        return (provisionApp.length == 2);
+    }
+
+    @Override
     public void setHotspotEnabled(boolean enabled) {
         final ContentResolver cr = mContext.getContentResolver();
         // This needs to be kept up to date with Settings (WifiApEnabler.setSoftapEnabled)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index bbe6622..eb808c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -571,7 +571,7 @@
                 cancel();
             } else {
                 dismiss();
-                UserInfo user = mUserManager.createUser(
+                UserInfo user = mUserManager.createSecondaryUser(
                         mContext.getString(R.string.user_new_user_name), 0 /* flags */);
                 if (user == null) {
                     // Couldn't create user, most likely because there are too many, but we haven't
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 86cfdb9..fca13f8 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2207,9 +2207,15 @@
     private List<ResolveInfo> queryIntentReceivers(Intent intent, int userId) {
         final long identity = Binder.clearCallingIdentity();
         try {
+            int flags = PackageManager.GET_META_DATA;
+
+            // Widgets referencing shared libraries need to have their
+            // dependencies loaded.
+            flags |= PackageManager.GET_SHARED_LIBRARY_FILES;
+
             return mPackageManager.queryIntentReceivers(intent,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    PackageManager.GET_META_DATA, userId);
+                    flags, userId);
         } catch (RemoteException re) {
             return Collections.emptyList();
         } finally {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 26e0db3..0cf22493 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -119,7 +119,7 @@
 
     private static final int MIN_USER_ID = 10;
 
-    private static final int USER_VERSION = 4;
+    private static final int USER_VERSION = 5;
 
     private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
 
@@ -462,6 +462,17 @@
         }
     }
 
+    /**
+     * If default guest restrictions haven't been initialized yet, add the basic
+     * restrictions.
+     */
+    private void initDefaultGuestRestrictions() {
+        if (mGuestRestrictions.isEmpty()) {
+            mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);
+            writeUserListLocked();
+        }
+    }
+
     @Override
     public Bundle getDefaultGuestRestrictions() {
         checkManageUsersPermission("getDefaultGuestRestrictions");
@@ -693,6 +704,11 @@
             userVersion = 4;
         }
 
+        if (userVersion < 5) {
+            initDefaultGuestRestrictions();
+            userVersion = 5;
+        }
+
         if (userVersion < USER_VERSION) {
             Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
                     + USER_VERSION);
@@ -715,6 +731,7 @@
         mUserRestrictions.append(UserHandle.USER_OWNER, restrictions);
 
         updateUserIdsLocked();
+        initDefaultGuestRestrictions();
 
         writeUserListLocked();
         writeUserLocked(primary);
diff --git a/tests/SharedLibrary/client/AndroidManifest.xml b/tests/SharedLibrary/client/AndroidManifest.xml
index a39c754..d1167fa 100644
--- a/tests/SharedLibrary/client/AndroidManifest.xml
+++ b/tests/SharedLibrary/client/AndroidManifest.xml
@@ -25,5 +25,13 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <receiver android:name="DependentAppwidgetProvider">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                       android:resource="@xml/dependent_appwidget_info" />
+        </receiver>
     </application>
 </manifest>
diff --git a/tests/SharedLibrary/client/res/layout/dependent_appwidget.xml b/tests/SharedLibrary/client/res/layout/dependent_appwidget.xml
new file mode 100644
index 0000000..13beafa
--- /dev/null
+++ b/tests/SharedLibrary/client/res/layout/dependent_appwidget.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <TextView android:id="@+id/label"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@com.google.android.test.shared_library:string/shared_string"
+        style="@com.google.android.test.shared_library:style/CodeFont"/>
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@com.google.android.test.shared_library:drawable/size_48x48"/>
+</LinearLayout>
diff --git a/tests/SharedLibrary/client/res/xml/dependent_appwidget_info.xml b/tests/SharedLibrary/client/res/xml/dependent_appwidget_info.xml
new file mode 100644
index 0000000..452e010
--- /dev/null
+++ b/tests/SharedLibrary/client/res/xml/dependent_appwidget_info.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="40dp"
+    android:minHeight="40dp"
+    android:updatePeriodMillis="0"
+    android:initialLayout="@layout/dependent_appwidget"
+    android:resizeMode="horizontal|vertical" />
diff --git a/tests/SharedLibrary/client/src/com/google/android/test/lib_client/DependentAppwidgetProvider.java b/tests/SharedLibrary/client/src/com/google/android/test/lib_client/DependentAppwidgetProvider.java
new file mode 100644
index 0000000..4e9b26b
--- /dev/null
+++ b/tests/SharedLibrary/client/src/com/google/android/test/lib_client/DependentAppwidgetProvider.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2013 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.google.android.test.lib_client;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class DependentAppwidgetProvider extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+
+    }
+}
diff --git a/tests/SharedLibrary/lib/res/values/public.xml b/tests/SharedLibrary/lib/res/values/public.xml
index 37b1ec9..23d307e 100644
--- a/tests/SharedLibrary/lib/res/values/public.xml
+++ b/tests/SharedLibrary/lib/res/values/public.xml
@@ -13,5 +13,7 @@
     <public type="attr" name="zip"          id="0x00010005" />
     <public type="attr" name="country"      id="0x00010006" />
 
+    <public type="drawable" name="size_48x48" id="0x00030000" />
+
     <public type="array" name="animals"     id="0x02050000" />
 </resources>