diff options
| -rw-r--r-- | Android.mk | 1 | ||||
| -rw-r--r-- | core/java/android/nfc/INfcAdapter.aidl | 1 | ||||
| -rw-r--r-- | core/java/android/nfc/INfcLockscreenDispatch.aidl | 12 | ||||
| -rw-r--r-- | core/java/android/os/UserManager.java | 33 | ||||
| -rw-r--r-- | media/java/android/media/AudioRecord.java | 40 | ||||
| -rw-r--r-- | media/java/android/media/AudioService.java | 118 | ||||
| -rw-r--r-- | media/java/android/media/IAudioService.aidl | 2 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/doze/DozeLog.java | 12 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/doze/DozeService.java | 176 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java | 8 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java | 2 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/UserManagerService.java | 19 |
12 files changed, 368 insertions, 56 deletions
@@ -187,7 +187,6 @@ LOCAL_SRC_FILES += \ 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/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.IAppCallback; 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 @@ public class UserManager { } /** + * 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 @@ package android.media; 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 @@ public class AudioRecord 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 @@ public class AudioRecord * AudioAttributes */ private AudioAttributes mAudioAttributes; + private boolean mIsSubmixFullVolume = false; //--------------------------------------------------------- // Constructor, Finalize @@ -267,6 +275,18 @@ public class AudioRecord 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 @@ public class AudioRecord @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 @@ public class AudioRecord // start recording synchronized(mRecordingStateLock) { if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) { + handleFullVolumeRec(true); mRecordingState = RECORDSTATE_RECORDING; } } @@ -608,6 +630,7 @@ public class AudioRecord // start recording synchronized(mRecordingStateLock) { if (native_start(syncEvent.getType(), syncEvent.getAudioSessionId()) == SUCCESS) { + handleFullVolumeRec(true); mRecordingState = RECORDSTATE_RECORDING; } } @@ -625,11 +648,25 @@ public class AudioRecord // 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 @@ public class AudioRecord 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 5c2abc5..96ce2c1 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -485,6 +485,7 @@ public class AudioService extends IAudioService.Stub { 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 @@ public class AudioService extends IAudioService.Stub { } } + 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 @@ public class AudioService extends IAudioService.Stub { 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 @@ public class AudioService extends IAudioService.Stub { 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 @@ public class AudioService extends IAudioService.Stub { 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 @@ public class AudioService extends IAudioService.Stub { 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 @@ interface IAudioService { 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 @@ public class DozeLog { 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 @@ public class DozeLog { 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 class DozeLog { } } + 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 @@ public class DozeLog { 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.Intent; 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 @@ public class DozeService extends DreamService { 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 @@ public class DozeService extends DreamService { // 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 @@ public class DozeService extends DreamService { } 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 @@ public class DozeService extends DreamService { } 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 @@ public class DozeService extends DreamService { 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 class DozeService extends DreamService { } 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 @@ public class DozeService extends DreamService { // 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 @@ public class DozeService extends DreamService { } } } + + 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/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 @@ public class EmptyShadeView extends StackScrollerDecorView { } @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/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 @@ public class UserSwitcherController { 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/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 26e0db3..0cf2249 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 @@ public class UserManagerService extends IUserManager.Stub { 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 @@ public class UserManagerService extends IUserManager.Stub { } } + /** + * 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 @@ public class UserManagerService extends IUserManager.Stub { 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 @@ public class UserManagerService extends IUserManager.Stub { mUserRestrictions.append(UserHandle.USER_OWNER, restrictions); updateUserIdsLocked(); + initDefaultGuestRestrictions(); writeUserListLocked(); writeUserLocked(primary); |
